一尘不染

TreeSet提供错误的输出-Java8

java

在使用树集时,我发现了非常奇怪的行为。

根据我的理解,以下程序应打印两条相同的行:

public class TestSet {
    static void test(String... args) {
        Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
        s.addAll(Arrays.asList("a", "b"));
        s.removeAll(Arrays.asList(args));
        System.out.println(s);
    }

    public static void main(String[] args) {
        test("A");
        test("A", "C");
    }
}

但奇怪的是它打印:

[b]
[a, b]

我无法理解-为什么树集的行为如此?


阅读 150

收藏
2020-12-03

共1个答案

一尘不染

发生这种情况是因为SortedSet的Comparator用于排序,但是removeAll依赖于equals每个元素的方法。从SortedSet文档中

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

可比文档中定义了“等于”的解释:

一类的自然顺序C被说成是 与equals一致 当且仅当e1.compareTo(e2) == 0具有相同的布尔值的e1.equals(e2)每一个e1e2阶级的C。请注意,null不是任何类的实例,并e.compareTo(null)应抛出NullPointerException,即使e.equals(null)回报false

强烈建议(尽管不是必需的)自然顺序应与等号保持一致。之所以如此,是因为没有显式比较器的排序集(和排序映射)在与自然排序与等式不一致的元素(或键)一起使用时,表现为“奇怪”。特别是,这样的排序集(或排序图)违反了根据equals方法定义的集合(或图)的一般约定。

总之,集合的比较器的行为与元素的equals方法不同,从而导致异常(尽管可预测)行为。

2020-12-03