在使用树集时,我发现了非常奇特的行为.根据我的理解,这个程序应该打印两个相同的行:
public class TestSet {
static void test(String... args) {
Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
s.addAll(Arrays.asList("a", "b"));
s.removeAll(Arrays.asList(args));
System.out.println(s);
}
public static void main(String[] args) {
test("A");
test("A", "C");
}
}
Run Code Online (Sandbox Code Playgroud)
但奇怪的是它打印:
[b]
[a, b]
Run Code Online (Sandbox Code Playgroud)
为什么树集会表现得像这样?
VGR*_*VGR 40
这是因为SortedSet的Comparator用于排序,但removeAll依赖于equals每个元素的方法.从SortedSet文档:
请注意,如果有序集合要正确实现接口,则由有序集合维护的排序(无论是否提供显式比较器)必须与equals一致
Set.(请参阅Comparable接口或Comparator接口以获得与equals一致的精确定义.)这是因为Set接口是根据equals操作定义的,但是有序集使用其compareTo(或compare)方法执行所有元素比较,因此两个元素是从排序集的角度来看,这种方法被认为是相等的.即使排序与equals不一致,排序集的行为也是明确定义的; 它只是不遵守Set界面的一般合同.
可比文档中定义了"与equals一致"的解释:
一类的自然顺序
C被说成是与equals一致当且仅当e1.compareTo(e2) == 0具有相同的布尔值的e1.equals(e2)每一个e1和e2阶级的C.请注意,这null不是任何类的实例,并且e.compareTo(null)应该抛出一个NullPointerException偶数e.equals(null)返回false.强烈建议(尽管不要求)自然排序与equals一致.这是因为没有显式比较器的有序集(和有序映射)在与自然顺序与equals不一致的元素(或键)一起使用时表现得"奇怪".特别地,这样的有序集(或有序映射)违反了集合(或映射)的一般契约,其根据该
equals方法定义.
总之,Set的比较器的行为与元素的equals方法不同,导致异常(虽然可预测)行为.
Sha*_*dov 15
嗯,这让我感到惊讶,我不知道我是否正确,但看看这个实现AbstractSet:
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(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;
}
Run Code Online (Sandbox Code Playgroud)
基本上在您的示例中,set的大小等于要删除的参数的大小,因此调用else条件.在那种情况下,检查你的参数集合是否删除contains了迭代器的当前元素,并且该检查区分大小写,因此它检查是否c.contains("a")返回false,因为c包含"A",而不是"a",因此元素不会被删除.请注意,当您向集合添加元素时,s.addAll(Arrays.asList("a", "b", "d"));它可以正常工作,因为size() > c.size()现在是正确的,因此不再进行contains检查.
| 归档时间: |
|
| 查看次数: |
1542 次 |
| 最近记录: |