下面显示的代码确实输出:
[b]
[a, b]
但是我希望它在输出中打印两行相同的行。
import java.util.*;
public class Test{
static void test(String... abc) {
Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
s.addAll(Arrays.asList("a", "b"));
s.removeAll(Arrays.asList(abc));
System.out.println(s);
}
public static void main(String[] args) {
test("A");
test("A", "C");
}
}
规范明确指出 removeAll
所以根据我的理解,当前的行为是不可预测的。请帮助我理解这一点
最佳答案
这与TreeSet<E>
的实现不一致,与bug接壤。当您传递给 removeAll
的集合中的项目数大于或等于集合中的项目数时,代码将忽略自定义比较器。
不一致是由一个小的优化引起的:如果您查看继承自 removeAll
的 AbstractSet
的 implementation ,则优化如下:
public boolean removeAll(Collection<?> c) {
boolean modified = false;
if (size() > c.size()) {
for (Iterator<?> i = c.iterator(); i.hasNext(); )
modified |= remove(i.next());
} else {
for (Iterator<?> i = iterator(); i.hasNext(); ) {
if (c.contains(i.next())) {
i.remove();
modified = true;
}
}
}
return modified;
}
您可以看到,当
c
的项目少于此集合(顶部分支)与具有相同数量或更多项目(底部分支)时,行为是不同的。顶部分支使用与该集合关联的比较器,而底部分支使用
equals
进行比较 c.contains(i.next())
- 全部采用相同的方法!您可以通过向原始树集添加一些额外元素来演示此行为:
s.addAll(Arrays.asList("x", "z", "a", "b"));
现在两个测试用例的输出变得相同,因为
remove(i.next())
使用了集合的比较器。关于java - AbstractSet.removeAll() 方法无法正常工作,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43121742/