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

2022-09-02 04:18:08

同时使用 Java 8 和 Java 11,请考虑以下带有 String::compareToIgnoreCase 比较器的 TreeSet

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)

答案 1

以下是 removeAll() 的 javadoc:

此实现通过调用每个集合的 size 方法来确定此集合和指定集合中哪个较小。如果此集合具有较少的元素,则实现将循环访问此集合,依次检查迭代器返回的每个元素,以查看它是否包含在指定的集合中。如果它被如此包含,则使用迭代器的 remove 方法将其从此集中删除。如果指定的集合具有较少的元素,则实现将循环访问指定的集合,并使用此集合的 remove 方法从此集合中删除迭代器返回的每个元素。

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

请注意,有一个关于此的错误:[JDK-8180409] TreeSet remove所有与String.CASE_INSENSITIVE_ORDER不一致的行为


答案 2

推荐