当前位置: 首页 > 知识库问答 >
问题:

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

沈运恒
2023-03-14

极端Java新手。我正在做一些简单的练习,以便练习语言的基本概念。

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

由于每个MusicTrack都必须通过其id进行唯一标识,并且考虑到所述列表必须“排序”(尽管没有实际的指示),我选择了树集。

因此,我在MusicTrack类中实现了Comparable,以便MusicBum的集合将根据它包含的MusicTrack实例的ID进行排序。另外,两个具有相同id的MusicTrack实例将被视为相同的MusicTrack实例,这样树集中就不会有重复的实例。到目前为止还不错(至少我认为是这样)。

当练习要求将MusicAlbum类按持续时间的降序(这是MusicTrack类的另一个属性)设置为可启用时,就会出现问题。

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

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

我发现这句话可能暗示这样的事情,如果可能的话,仍然不被推荐:

请注意,如果此排序映射要正确实现映射接口,则由排序映射维护的排序(无论是否提供显式比较器)必须与equals一致。(参见可比或比较器,了解与等于一致的精确定义。)这是因为映射接口是根据equals操作定义的,但映射使用其compareTo(或compare)方法执行所有键比较,因此从排序映射的角度来看,此方法认为相等的两个键是相等的。排序映射的行为定义良好,即使其顺序与equals不一致;只是没有遵守地图界面的总合同。

然而,我发现关于这一点的信息让我很困惑,所以我要求澄清。

还有,解决这个问题的好方法是什么?当然,到目前为止,我做到了以下几点:

音乐rack.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;
    }
}

音乐迷。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个答案

谷梁煌
2023-03-14
  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;
    }
}
public Iterator<MusicTrack> iterator() {
    TreeSet<MusicTrack> temp = new TreeSet<MusicTrack>(new DurationComparator());
    temp.addAll(tracks);
    return temp.iterator();
}

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


(注意,我添加了方法getTracks()到类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成为:

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

现在看看当你列出一张专辑中两个或更多曲目持续时间相同的曲目时会发生什么。

 类似资料:
  • 问题内容: 这是我用于Java 5.0的代码片段 Collections.reverseOrder() 用于获取比较器,以反转元素的存储和迭代方式。 有没有更优化的方法呢? 问题答案: 您为什么认为这种方法不会得到优化?相反的顺序简单地将被翻转从实际输出的符号(或输出上的物体插入),因此我会想象它是非常快的。 另一个建议:与其更改存储元素的顺序,不如使用该方法以降序迭代它们。

  • 问题内容: 假设我有两个类CLassA和CLassB。它们有一个共同的属性,例如每个类拥有的元素数量。 我如何从ClassA和CLassB的对象创建一个集合,并按该属性排序(降序升序无所谓)? 我收集了一个类型,但是当我尝试实现Comparable Interface时,我无法使用该方法(例如,获取返回元素nr的get)。 我有什么解决方案? 谢谢你的帮助! 问题答案: 实际上,如果要将它们放在同

  • 主要内容:算法总结及实现,优化算法在实际开发中,有很多场景需要我们将数组元素按照从大到小(或者从小到大)的顺序排列,这样在查阅数据时会更加直观,例如: 一个保存了班级学号的数组,排序后更容易分区好学生和坏学生; 一个保存了商品单价的数组,排序后更容易看出它们的性价比。 对数组元素进行排序的方法有很多种,比如冒泡排序、归并排序、选择排序、插入排序、快速排序等,其中最经典最需要掌握的是「冒泡排序」。 以从小到大排序为例,冒泡排序的整体

  • 问题内容: 在我正在使用的代码下面,可以正常工作并输出名称,但不能使用sort方法。我期望“ Collections.sort(nameFromText);” 按名字的字母顺序对ArrayList进行排序。 我究竟做错了什么? 问题答案: 方法期望要排序的列表元素具有可比性。元素类型应该实现接口,或者您应该使用带有通用实例的重载方法。 在下面的代码中,您不满足上述两个条件。您的类既没有实现,也没有

  • 我试图写一个函数来排序一个对象集合。由于对象都是相同的类型(相同的用户定义类),因此它们的属性集是相同的。是否有可能(通过代码)发现对象的属性,以便将集合放在一个二维数组中,每行代表一个对象,每列代表它的一个属性? 另一种解决方案是将集合中的每个对象复制到对象数组中,并根据它们的一个属性对它们进行排序,该属性的名称作为字符串传递给函数。但是我不知道如何使用作为字符串传递的属性名来指向对象的属性。

  • 问题内容: 更新进度条时对集合排序的最佳方法是什么?目前,我有这样的代码: 这显示进度,但是进度条随着项目数量的增加而减慢。有谁有更好的方法?理想情况下,我想使用类似于的接口,以便尝试不同的排序算法。 任何帮助将是巨大的! 作为背景,这段代码正在从Lucene撤回许多文档(1到1000万个),并在它们之上运行自定义比较器。通过将数据写回到磁盘上对它们进行排序将太慢而无法实用。大部分成本是从磁盘上读