为什么不使用自定义比较器从 TreeSet 中删除会删除更大的项目集?

Why doesn't removing from a TreeSet with a custom comparator remove a larger set of items?

同时使用 Java 8 和 Java 11,考虑以下 TreeSet with a String::compareToIgnoreCase 比较器:

final Set<String> languages = new TreeSet<>(String::compareToIgnoreCase);
languages.add("java");
languages.add("c++");
languages.add("python");

System.out.println(languages);                 // [c++, java, python]

当我尝试删除 TreeSet 中存在的确切元素时,它起作用了:所有指定的元素都被删除了:

languages.removeAll(Arrays.asList("PYTHON", "C++"));

System.out.println(languages);                 // [java]

但是,如果我尝试删除比 TreeSet 中存在的 更多 ,该调用根本不会删除任何内容(这不是后续的调用但调用而不是上面的代码片段):

languages.removeAll(Arrays.asList("PYTHON", "C++", "LISP"));

System.out.println(languages);                 // [c++, java, python]

我做错了什么?为什么会这样?

编辑:String::compareToIgnoreCase 是一个有效的比较器:

(l, r) -> l.compareToIgnoreCase(r)

这是 removeAll() 的 javadoc:

This implementation determines which is the smaller of this set and the specified collection, by invoking the size method on each. If this set has fewer elements, then the implementation iterates over this set, checking each element returned by the iterator in turn to see if it is contained in the specified collection. If it is so contained, it is removed from this set with the iterator's remove method. If the specified collection has fewer elements, then the implementation iterates over the specified collection, removing from this set each element returned by the iterator, using this set's remove method.

在您的第二个实验中,您处于 javadoc 的第一个案例中。因此它遍历 "java"、"c++" 等并检查它们是否包含在 Set.of("PYTHON", "C++") 返回的集合中。他们不是,所以他们没有被删除。 使用另一个使用相同比较器作为参数的 TreeSet,它应该可以正常工作。 使用两种不同的 Set 实现,一种使用 equals(),另一种使用比较器,这确实是一件危险的事情。

请注意,有一个关于此的错误:[JDK-8180409] TreeSet removeAll inconsistent behaviour with String.CASE_INSENSITIVE_ORDER