Java TreeSet与长度比较器错误?

时间:2018-08-08 20:43:01

标签: java comparator treeset

我有以下代码,该代码根据字符串长度使用比较器创建TreeSet。

public class TreeSetComparator {
    public static void main(String[] args) {
        SortedSet<String> sortedSet = new TreeSet<>(Comparator.comparing(String::length));
        sortedSet.addAll(Arrays.asList("aa", "bb", "aa"));
        System.out.println(sortedSet);
    }
}

令我惊讶的是,上面的输出是

[aa]

虽然我期望

[aa, bb]

[bb, aa]

“ bb”部分消失了,这似乎与SortedSet合同相反。比较器应该只对元素排序,而不能确定它们的唯一性,通常由等式确定。

另一方面,如果我将比较器增强为对于如下所示的不相等项始终返回非零值,则只有这样才能获得正确的结果。

    SortedSet<String> sortedSet = new TreeSet<>(Comparator.comparing(String::length).reversed().thenComparing(String::toString));
    sortedSet.addAll(Arrays.asList("aa", "bb", "aa"));
    System.out.println(sortedSet);

正如我所期望的那样,现在的输出为[aa, bb]

以上是TreeSet实现中的错误吗?

我的环境如下:

mvn --version                                                                                                                                            21:40:22
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-17T19:33:14+01:00)
Maven home: /home/aaaa/.sdkman/candidates/maven/current
Java version: 10.0.2, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-10-jdk
Default locale: en_GB, platform encoding: UTF-8
OS name: "linux", version: "4.14.60-1-manjaro", arch: "amd64", family: "unix"

更新

此处是相关文章,以及有关如何在Java的未来版本中解决此问题的建议:https://yesday.github.io/blog/2018/java-gotchas-sorted-set-ignores-the-equals-method.html

2 个答案:

答案 0 :(得分:14)

这不是错误。至少不在By definition中。

在javadoc中,我强调:

  

请注意,集合维护的排序(无论是否显式   提供比较器),如果要等于   正确实现Set接口。 (请参阅可比或比较器   才能精确定义与equals的一致性。)   因为Set接口是根据equals操作定义的,   但是 TreeSet实例使用其实例执行所有元素比较   compareTo(或compare)方法,因此两个元素被视为相等   从集合的角度来看,这种方法等于。的   集合的行为是明确定义的,即使其顺序不一致   等于它只是不服从套装的总合约   界面。

因此,由于“ aa”和“ bb”的长度均为2,因此它们被compareToTreeSet视为相等。

This thread等于等值表示:

  

当且仅当c.compare(e1,e2)== 0具有相同的布尔值时,比较器c对一组元素S施加的排序被称为<等于> 为S中每个e1和e2的e1.equals(e2)。

答案 1 :(得分:0)

看起来他们假设比较器使用与equals方法相同的equals定义。通过SortedSet API:

  

请注意,如果排序集要正确实现Set接口,则排序集(无论是否提供显式比较器)所维护的顺序必须与equals一致。 (有关与equals一致的精确定义,请参见Comparable接口或Comparator接口。)之所以这样,是因为Set接口是根据equals操作定义的,但是排序后的set使用其compareTo(或compare)方法执行所有元素比较。 ,因此从排序集的角度来看,此方法认为相等的两个元素相等。即使排序集的排序与equals不一致,也可以很好地定义其行为。只是不能遵守Set接口的常规协定。