【问题标题】:Java - TreeSet accepting duplicatesJava - TreeSet 接受重复项
【发布时间】:2018-05-02 23:14:43
【问题描述】:

我在使用 TreeSet 时遇到了一些问题:为什么这个接受重复项?我认为 TreeSets 通过比较器检测到它们,并自动删除它们。请帮助我,我对 Java 和 StackOverflow 都很陌生。

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class SortedSongs
{   
    private Set songs;
    public SortedSongs()
    {
         Comparator<Song> comp = (Song c1, Song c2)-> c1.toString().compareTo(c2.toString());
         songs = new TreeSet<>(comp);
    }
}

编辑:这就是我实现 hashCode 和 equals 的方式:

@Override
public int hashCode()
{
    return Objects.hash(name, author);
}

@Override
public boolean equals(Object o)
{
    return o == null ? false : o.getClass() != getClass() ? false
        : o.hashCode() == hashCode();
}

编辑2: 这是 Song 类的更新后的 equals 方法、toString 和 compareTo

@Override
public boolean equals(Object o)
{
    if (this==o) return true;
    if (getClass()!=o.getClass()) return false;
    return name.equals(((Song) o).name) && author.equals(((Song) o).author);
}
@Override
public String toString() {return name + " - " + author;}

public int compareTo(Song other)
{
    if (name.equals(other.name))
        return author.equals(other.author) ? 0 : author.compareTo(other.author);
    return name.compareTo(other.name);
}

所以现在是 SortedSongs 中的比较器

Comparator<Song> comp = (Song c1, Song c2)-> c1.compareTo(c2);

仍然无法正常工作,我觉得好像我错过了一些明显的东西

编辑3: 解决了,我实际上在我的测试课上犯了一个错误。尴尬。抱歉,不是故意浪费您的时间,希望这对某人有所帮助。

【问题讨论】:

  • 他们使用哈希码和等号来查看是否在集合中。
  • 问题可能有点微妙:您正在通过字符串表示来比较歌曲。您应该向我们展示toString 是如何实现的,并且最好还展示哪些Song 对象最终会重复。一般来说,比较器必须与equals一致以遵守Set接口的约定,但即使没有正确的equals实现,您也不应该看到重复。
  • (附注:您当前的equals 实现是not 有效的。一般规则是:If 对象根据equals 方法,那么它们必须具有相同的hashCode。但是如果它们具有相同的hashCode,那么根据@987654333,它们不一定相等@ 方法。只是一个旁注,因为它应该与您正在观察的问题无关)
  • 您的equalsComparator&lt;Song&gt; 不太正确。尝试先修复它们,以便它们逐个字段比较 Songs。
  • equals方法是绝对错误的。 Joshua Bloch 在“Effective Java”的第 3 章中告诉您如何正确覆盖 equals 和 hashCode。

标签: java comparator treeset


【解决方案1】:

TreeSet 是用 Java 中的平衡二叉树(实际上是红黑树)实现的。所以它不使用equals 方法。它使用Comparator

现在关于你的实现的问题是关于你的comparator。 您的比较器基于toString 方法。默认情况下,java 返回对象类的名称及其哈希码。因此,默认情况下,当且仅当它们指向相同的内存引用时,toString 的输出对于两个对象将是相同的。您需要确保在您的类中覆盖了 toString 方法,因为您的比较器基于该方法。

要解决此问题,您需要定义一个反映程序比较逻辑的比较器。

【讨论】:

  • 这个问题显然与toString有关,但只要我们不知道toString是否(或如何)被覆盖,这里很难给出明确的答案。根据名称 (SortedSongs),我很确定有一个 toString 实现,其目的是按字母顺序对歌曲进行排序 - 但这也只是一个猜测......
  • 根本不要使用toString()(对于任何事情永远)。让Comparator 在没有toString() 的任何帮助的情况下完成自己的工作,直接查看包含Song 的任何数据——标题、作家、歌手、编曲者等等——然后不管toString()是对还是错,是否被覆盖..
【解决方案2】:

TreeSet 在后台委托TreeMap 使用虚拟值实现。 因此,与其允许使用复杂的比较器进行重复,不如切换到包含重复计数的值的 TreeMap 会更容易且内存效率更高。

这是一个示例:How do I use a TreeSet in Java that allows Duplicates?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 2015-12-29
    • 2011-10-05
    • 1970-01-01
    • 2012-04-08
    • 2012-08-18
    • 1970-01-01
    相关资源
    最近更新 更多