【问题标题】:Why doesn't removing from a TreeSet with a custom comparator remove a larger set of items?为什么不使用自定义比较器从 TreeSet 中删除更大的项目集?
【发布时间】:2020-04-20 12:14:16
【问题描述】:

同时使用 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]

但是,如果我尝试删除 more 而不是 TreeSet 中的内容,则调用根本不会删除任何内容(这不是后续调用,而是调用而不是 sn -p 以上):

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

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

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

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

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

【问题讨论】:

标签: java java-8 comparator java-11 treeset


【解决方案1】:

这是removeAll()的javadoc:

此实现通过在每个集合上调用 size 方法来确定该集合和指定集合中的较小者。如果这个集合的元素较少,则实现迭代这个集合,依次检查迭代器返回的每个元素,看它是否包含在指定的集合中。如果它如此包含,则使用迭代器的 remove 方法将其从该集合中删除。如果指定集合的​​元素较少,则实现迭代指定集合,使用此集合的 remove 方法从此集合中删除迭代器返回的每个元素。

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

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

【讨论】:

  • 你的意思是当两个集合具有相同的特征时,它会起作用吗? final Set&lt;String&gt; subLanguages = new TreeSet&lt;&gt;(String::compareToIgnoreCase);subLanguages.addAll(Arrays.asList("PYTHON", "C++", "LISP"));languages.removeAll(subLanguages);
  • 你的情况是“如果这个集合有更少的元素”,由 javadoc 描述。另一种情况是“如果指定集合的​​元素较少”。
  • 这个答案是对的,但这是非常不直观的行为。感觉就像TreeSet的设计有缺陷。
  • 我同意,但对此我无能为力。
  • 两者兼而有之:这是一种非常不直观的行为,但已被正确记录,但由于不直观且具有欺骗性,它也是一个设计错误,有朝一日可能会被修复。
猜你喜欢
  • 2014-03-08
  • 2014-08-13
  • 1970-01-01
  • 2011-12-26
  • 2013-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多