为什么列表中的Java removeAll方法要删除重复项? @测试 公共无效测试4(){ 列表测试=新建ArrayList(); 测试。添加(“A”); 测试。添加(“B”); 测试。添加(“C”); 测试。添加(“C”); System.out.println(“完整列表…”); 用于(字符串s:测试){ 系统输出打印项次; } System.out.println(“tmp列表…”); 列表tmp=测试子列表(0,1); for(字符串s:tmp){ 系统输出打印项次; } System.out.println(“删除tmp后…”); 测试移除所有(tmp); 用于(字符串s:测试){ 系统输出打印项次; } }

为什么列表中的Java removeAll方法要删除重复项? @测试 公共无效测试4(){ 列表测试=新建ArrayList(); 测试。添加(“A”); 测试。添加(“B”); 测试。添加(“C”); 测试。添加(“C”); System.out.println(“完整列表…”); 用于(字符串s:测试){ 系统输出打印项次; } System.out.println(“tmp列表…”); 列表tmp=测试子列表(0,1); for(字符串s:tmp){ 系统输出打印项次; } System.out.println(“删除tmp后…”); 测试移除所有(tmp); 用于(字符串s:测试){ 系统输出打印项次; } },java,Java,上述代码输出 @Test public void test4() { List<String> test = new ArrayList<String>(); test.add("A"); test.add("B"); test.add("C"); test.add("C"); System.out.println("full list ...." ); for (String s : test) {

上述代码输出

@Test
public void test4() {

    List<String> test = new ArrayList<String>();

    test.add("A");
    test.add("B");
    test.add("C");
    test.add("C");

    System.out.println("full list ...." );

    for (String s : test) {

        System.out.println(s);
    }

    System.out.println("tmp list ...." );

    List<String> tmp = test.subList( 0, 1) ;

    for (String s : tmp) {

        System.out.println(s);
    }

    System.out.println("after removing tmp ...." );

    test.removeAll( tmp );

    for (String s : test) {

        System.out.println(s);
    }

}
完整列表。。。。 A. B C C tmp列表。。。。 A. 在删除tmp。。。。 B C C 如果
List tmp=test.subList(0,2)

然后输出为

full list .... A B C C tmp list .... A after removing tmp .... B C C 完整列表。。。。 A. B C C tmp列表。。。。 A. B 在删除tmp。。。。 C
为什么名单上只有一个“C”?

我的答案很短,而且获得了很多赞成票,所以我觉得有必要改进它

调用
子列表时,不会得到副本。你可以看到。每当修改视图的底层结构(在本例中是原始的
列表
)时,该视图立即变为无效。但是,这并没有说明使用视图的结果,只是说明结果可能有效,也可能无效。该行为未定义。这就像在C中释放内存。如果您有一个指向刚刚释放的内存的指针,您仍然可以访问数据。但是,您已经告诉操作系统您不再需要内存空间,因此它可以随时被覆盖。这里也一样。它可能有效,也可能无效,因为行为未定义


因此,您需要做的是制作内容的正确副本。您可以使用新的ArrayList(test.subList(0,2))来实现这一点。

我的答案很短,而且获得了很多赞成票,因此我觉得需要改进它

调用
子列表时,不会得到副本。你可以看到。每当修改视图的底层结构(在本例中是原始的
列表
)时,该视图立即变为无效。但是,这并没有说明使用视图的结果,只是说明结果可能有效,也可能无效。该行为未定义。这就像在C中释放内存。如果您有一个指向刚刚释放的内存的指针,您仍然可以访问数据。但是,您已经告诉操作系统您不再需要内存空间,因此它可以随时被覆盖。这里也一样。它可能有效,也可能无效,因为行为未定义


因此,您需要做的是制作内容的正确副本。您可以使用
newarraylist(test.subList(0,2))

执行此操作。请参阅@Aominè上的java文档,删除指定集合中也包含的此集合的所有元素(可选操作)。此调用返回后,此集合将不包含与指定集合相同的元素。-这并不能解释为什么当子列表只包含
A
B
时,一个
C
也会被删除,因为它是一个视图,当您修改基础列表时,结果是不可预测的。Javadoc说:如果支持列表(即,此列表)在结构上被修改,而不是通过返回的列表,则此方法返回的列表的语义将变得未定义。--将
test.subList(0,2)
更改为
newarraylist(test.subList(0,2))
将解决此问题,因为它会快照视图。我希望Java返回错误,而不是不可靠的结果。它在第一种情况下有效(预期结果)。第二种情况会发生什么?您能详细解释一下吗。@PrajwalShrestha不,我不能解释,因为这就是“未定义”的意思。请参阅@Aominè上的java文档,删除指定集合中也包含的该集合的所有元素(可选操作)。此调用返回后,此集合将不包含与指定集合相同的元素。-这并不能解释为什么当子列表只包含
A
B
时,一个
C
也会被删除,因为它是一个视图,当您修改基础列表时,结果是不可预测的。Javadoc说:如果支持列表(即,此列表)在结构上被修改,而不是通过返回的列表,则此方法返回的列表的语义将变得未定义。--将
test.subList(0,2)
更改为
newarraylist(test.subList(0,2))
将解决此问题,因为它会快照视图。我希望Java返回错误,而不是不可靠的结果。它在第一种情况下有效(预期结果)。第二种情况会发生什么?你能详细解释吗?@PrajwalShrestha不,我不能解释,因为这就是“未定义”的意思。你能详细解释为什么它在第一种情况下有效而在第二种情况下无效吗。我知道子列表是一个视图。它是否有效取决于实现。行为是未定义的,因此它可能有效,也可能无效。您能否详细解释为什么它在第一种情况下有效,而在第二种情况下无效。我知道子列表是一个视图。它是否有效取决于实现。该行为未定义,因此可能有效,也可能无效。 full list .... A B C C tmp list .... A B after removing tmp .... C