简体   繁体   English

使用不同的标准对树集中的元素进行排序和区分

[英]Sorting and distinguishing elements within a treeset with different criterias

Extreme Java newbie here.这里是极限 Java 新手。 I'm doing some simple excercises in order to get some practice with the basic concepts of the language.我正在做一些简单的练习,以便对语言的基本概念进行一些练习。

One of these exercises asks me to implement a MusicAlbum class which, among other things, has as one of its instance attributes a list of instances of class MusicTrack.其中一个练习要求我实现一个 MusicAlbum 类,它的实例属性之一是类 MusicTrack 的实例列表。

Since every MusicTrack must be uniquely identified by its id, and considering that it is specified that said list must be "sorted" (although there's no real indication about it), I opted for a TreeSet.由于每个 MusicTrack 必须由其 id 唯一标识,并且考虑到指定所述列表必须“排序”(尽管没有真正的指示),我选择了 TreeSet。

So I've implemented Comparable in the MusicTrack class so that the MusicAlbum's set will be sorted by the ids of the MusicTrack's instances which it contains.因此,我在 MusicTrack 类中实现了 Comparable,以便 MusicAlbum 的集合将按其包含的 MusicTrack 实例的 id 进行排序。 Also, two MusicTrack instances with the same id will be considered the same MusicTrack instance and then there won't be duplicates within the treeset.此外,两个具有相同 id 的 MusicTrack 实例将被视为相同的 MusicTrack 实例,然后树集中不会有重复项。 So far so good (or at least I think so).到目前为止一切顺利(或者至少我是这么认为的)。

The problem arises when the exercise asks to make the MusicAlbum class iterable in a decreasing order of duration (which is another attribute of the MusicTrack class).当练习要求按持续时间递减的顺序(这是 MusicTrack 类的另一个属性)使 MusicAlbum 类可迭代时,就会出现问题。

I've immediately thought to modify the compareTo method so that the sorting of the treeset would instead be organised by duration, while an overriding of the equals method of class Object would still guarantee uniqueness by id.我立即想到修改 compareTo 方法,以便树集的排序改为按持续时间组织,而重写 Object 类的 equals 方法仍将保证按 id 的唯一性。 However, this did not work, it seems like the existence of the compareTo method makes the equals method completely irrelevant.但是,这不起作用,似乎 compareTo 方法的存在使 equals 方法完全无关紧要。

So here's my question: is it possible to sort a treeset with a criteria and to mantain uniqueness within that same treeset with a completely different criteria?所以这是我的问题:是否可以使用标准对树集进行排序,并使用完全不同的标准在同一树集中保持唯一性?

I've found this quote that might suggests that such thing, if even possible, is still not recommended:我发现这句话可能表明,即使可能,仍然不建议这样做:

Note that the ordering maintained by a sorted map (whether or not an explicit comparator is provided) must be consistent with equals if this sorted map is to correctly implement the Map interface.请注意,如果排序映射要正确实现 Map 接口,则排序映射(无论是否提供显式比较器)维护的顺序必须与 equals 一致。 (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal. (请参阅 Comparable 或 Comparator 以获得与等于一致的精确定义。)这是因为 Map 接口是根据等于操作定义的,但映射使用其 compareTo(或比较)方法执行所有键比较,因此两个键从排序映射的角度来看,被这种方法视为相等的对象是相等的。 The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals;排序映射的行为是明确定义的,即使它的排序与 equals 不一致; it just fails to obey the general contract of the Map interface.它只是不遵守 Map 接口的一般约定。

However the infos I've found about this was pretty confusing to me, so I'm asking for clarifications.然而,我发现的有关这方面的信息对我来说很困惑,所以我要求澄清。

Also what could be a good way to solve this exercise?还有什么是解决这个练习的好方法? Of course, here's what I managed to do so far:当然,这是我目前设法做到的:

MusicTrack.java音乐曲目

public class MusicTrack implements Comparable<MusicTrack> {

    private static int nextId = 0;

    private int id;
    private String title;
    private String author;
    private int duration;

    @SuppressWarnings("serial")
    public class NegativeDurationException extends Exception {

        public NegativeDurationException() {

            System.err.println("Duration value must be greater than 0.");
        }
    }

    public MusicTrack(String title, String author, int duration) throws NegativeDurationException {

        if(duration < 1) {

            throw new NegativeDurationException();
        }
        else {

            this.id = nextId++;
            this.title = title;
            this.author = author;
            this.duration = duration;
        }
    }

    public int getId() {

        return this.id;
    }

    public String getTitle() {

        return this.title;
    }

    public void setTitle(String title) {

        this.title = title;
    }

    public String getAuthor() {

        return this.author;
    }

    public void setAuthor(String author) {

        this.author = author;
    }

    public int getDuration() {

        return this.duration;
    }

    public void setDuration(int duration) {

        this.duration = duration;
    }

    public String toString() {

        return "Id: " + this.id  + "\nAuthor: " + this.author + "\nTitle: " + this.title + "\nDuration: " + this.duration + "\n";
    }

    @Override
    public int compareTo(MusicTrack track) {

        return this.id - track.id;
    }
}

MusicAlbum.java音乐专辑.java

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

public class MusicAlbum implements Iterable<MusicTrack> {

    public enum PhysicalMedia {

        VYNIL, CD, USB
    }

    private static int nextId = 0;

    private int id;
    private String title;
    private String author;
    private Date purchaseTime;
    private Set<MusicTrack> tracks;
    private PhysicalMedia physicalMedia;

    public MusicAlbum(String title, String author, String purchaseTime, PhysicalMedia physicalMedia) {

        try {

            this.purchaseTime = new SimpleDateFormat("dd/mm/yyyy").parse(purchaseTime);
        } 
        catch (ParseException e) {

            e.printStackTrace();
        }

        this.id = nextId++;
        this.title = title;
        this.author = author;
        this.physicalMedia = physicalMedia;
        this.tracks = new TreeSet<MusicTrack>();
    }

    public void addMusicTracks(MusicTrack ... tracks) {

        for(MusicTrack track: tracks) {

            this.tracks.add(track);
        }
    }

    public boolean contains(MusicTrack track) {

        return this.tracks.contains(track);
    }

    public int getTotalDuration() {

        Iterator<MusicTrack> i = this.tracks.iterator();
        int totalDuration = 0;

        while(i.hasNext()) {

            totalDuration += i.next().getDuration();
        }

        return totalDuration;
    }

    public String toString() {

        return "Id: " + this.id + "\nDate: " + this.purchaseTime.toString() + "\nTotal duration: " + this.getTotalDuration();
    }


    @Override
    public Iterator<MusicTrack> iterator() {

        return this.tracks.iterator();
    }

}
  1. Write a duration comparator.写一个持续时间比较器。
class DurationComparator implements Comparator<MusicTrack> {

    @Override
    public int compare(MusicTrack o1, MusicTrack o2) {
        int d1 = o1 == null ? 0 : o1.getDuration();
        int d2 = o2 == null ? 0 : o2.getDuration();
        return d2 - d1;
    }
}
  1. Change method iterator() in class MusicAlbum更改类MusicAlbum方法iterator()
public Iterator<MusicTrack> iterator() {
    TreeSet<MusicTrack> temp = new TreeSet<MusicTrack>(new DurationComparator());
    temp.addAll(tracks);
    return temp.iterator();
}

Now the iterator lists the tracks in order of decreasing duration, whereas simply listing the tracks displays them in order of ID.现在迭代器按持续时间递减的顺序列出曲目,而简单地列出曲目按 ID 的顺序显示它们。

Demonstrating code.演示代码。
(Note that I added method getTracks() to class MusicAlbum that returns tracks member.) (请注意,我将方法getTracks()添加到返回tracks成员的MusicAlbum类。)

public static void main(String[] args) throws NegativeDurationException {
    MusicAlbum album = new MusicAlbum("title", "author", "03/10/2003", PhysicalMedia.CD);
    MusicTrack track1 = new MusicTrack("title_1", "author_1", 30);
    MusicTrack track2 = new MusicTrack("title_2", "author_2", 40);
    MusicTrack track3 = new MusicTrack("title_3", "author_3", 10);
    MusicTrack track4 = new MusicTrack("title_4", "author_4", 20);
    album.addMusicTracks(track1, track2, track3, track4);
    Iterator<MusicTrack> iter = album.iterator();
    while (iter.hasNext()) {
        System.out.println(iter.next());
    }
    System.out.println("====================================================================");
    album.getTracks().forEach(System.out::println);
}

Output of above main() method:上述main()方法的输出:

Id: 1
Author: author_2
Title: title_2
Duration: 40

Id: 0
Author: author_1
Title: title_1
Duration: 30

Id: 3
Author: author_4
Title: title_4
Duration: 20

Id: 2
Author: author_3
Title: title_3
Duration: 10

====================================================================
Id: 0
Author: author_1
Title: title_1
Duration: 30

Id: 1
Author: author_2
Title: title_2
Duration: 40

Id: 2
Author: author_3
Title: title_3
Duration: 10

Id: 3
Author: author_4
Title: title_4
Duration: 20

EDIT编辑

Due to your comment, @Gian, I realize that you need a list iterator and not a set because there may be two or more MusicTrack s with the same duration.由于您的评论,@Gian,我意识到您需要一个列表迭代器而不是一个集合,因为可能有两个或多个MusicTrack具有相同的持续时间。 Hence method iterator() in class MusicAlbum becomes:因此,类MusicAlbum方法iterator()变为:

public Iterator<MusicTrack> iterator() {
    List<MusicTrack> temp = new ArrayList<MusicTrack>();
    temp.addAll(tracks);
    Collections.sort(temp, new DurationComparator());
    return temp.iterator();
}

Now see what happens when you list the tracks of an album where two or more tracks have the same duration.现在看看当您列出专辑中两首或多首曲目具有相同持续时间的曲目时会发生什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM