繁体   English   中英

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

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

这里是极限 Java 新手。 我正在做一些简单的练习,以便对语言的基本概念进行一些练习。

其中一个练习要求我实现一个 MusicAlbum 类,它的实例属性之一是类 MusicTrack 的实例列表。

由于每个 MusicTrack 必须由其 id 唯一标识,并且考虑到指定所述列表必须“排序”(尽管没有真正的指示),我选择了 TreeSet。

因此,我在 MusicTrack 类中实现了 Comparable,以便 MusicAlbum 的集合将按其包含的 MusicTrack 实例的 id 进行排序。 此外,两个具有相同 id 的 MusicTrack 实例将被视为相同的 MusicTrack 实例,然后树集中不会有重复项。 到目前为止一切顺利(或者至少我是这么认为的)。

当练习要求按持续时间递减的顺序(这是 MusicTrack 类的另一个属性)使 MusicAlbum 类可迭代时,就会出现问题。

我立即想到修改 compareTo 方法,以便树集的排序改为按持续时间组织,而重写 Object 类的 equals 方法仍将保证按 id 的唯一性。 但是,这不起作用,似乎 compareTo 方法的存在使 equals 方法完全无关紧要。

所以这是我的问题:是否可以使用标准对树集进行排序,并使用完全不同的标准在同一树集中保持唯一性?

我发现这句话可能表明,即使可能,仍然不建议这样做:

请注意,如果排序映射要正确实现 Map 接口,则排序映射(无论是否提供显式比较器)维护的顺序必须与 equals 一致。 (请参阅 Comparable 或 Comparator 以获得与等于一致的精确定义。)这是因为 Map 接口是根据等于操作定义的,但映射使用其 compareTo(或比较)方法执行所有键比较,因此两个键从排序映射的角度来看,被这种方法视为相等的对象是相等的。 排序映射的行为是明确定义的,即使它的排序与 equals 不一致; 它只是不遵守 Map 接口的一般约定。

然而,我发现的有关这方面的信息对我来说很困惑,所以我要求澄清。

还有什么是解决这个练习的好方法? 当然,这是我目前设法做到的:

音乐曲目

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;
    }
}

音乐专辑.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. 写一个持续时间比较器。
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. 更改类MusicAlbum方法iterator()
public Iterator<MusicTrack> iterator() {
    TreeSet<MusicTrack> temp = new TreeSet<MusicTrack>(new DurationComparator());
    temp.addAll(tracks);
    return temp.iterator();
}

现在迭代器按持续时间递减的顺序列出曲目,而简单地列出曲目按 ID 的顺序显示它们。

演示代码。
(请注意,我将方法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);
}

上述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

编辑

由于您的评论,@Gian,我意识到您需要一个列表迭代器而不是一个集合,因为可能有两个或多个MusicTrack具有相同的持续时间。 因此,类MusicAlbum方法iterator()变为:

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

现在看看当您列出专辑中两首或多首曲目具有相同持续时间的曲目时会发生什么。

暂无
暂无

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

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