下面显示的代码确实输出:

[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 的集合中的项目数大于或等于集合中的项目数时,代码将忽略自定义比较器。

不一致是由一个小的优化引起的:如果您查看继承自 removeAllAbstractSetimplementation ,则优化如下:

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/

10-10 14:39