2

需要对java.util.list. 我正在使用 eclipse 进行开发。
我写了这段代码

public static void main(String[] asdf){
        List<Integer> lst = new ArrayList<Integer>();
        for(int i=0;i<10000;i++){
            lst.add(i);
        }

        System.out.println(lst.size());

        for(int i=0;i<10000;i++){
            if((i%50)==0){
                lst.remove(i);
            }           
        }
        System.out.println(lst.size());

    }

但是当我运行这段代码时,它给出了异常

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 9850, Size: 9803
    at java.util.ArrayList.rangeCheck(ArrayList.java:604)
    at java.util.ArrayList.remove(ArrayList.java:445)
    at com.ilex.reports.action.rpt.CNSReports.main(CNSReports.java:301)

还有一点需要注意的是 在此处输入图像描述

然后我对代码进行了一次更改,即仅将第二个循环迭代到 5000 并且效果很好

在此处输入图像描述

问题是 为什么要给出 IndexOutOfBoundsException ?

那是什么 modCoutn ?

如果是的话,这件事是否会成为内存泄漏的原因如何解决?

提前致谢。

4

6 回答 6

16

从列表中删除元素会使其更小。您的第二个循环一直运行到 10000,但到那里时列表将缩小到小于 10000。

事实上,如果您的意图是删除所有 50 的倍数,您可以从 10000 向后循环到 0,步长为 50 并避免这个问题,并且更快。

for (int i=9950; i>=0; i-=50){
    lst.remove(i);
}

请注意,如果您的意图是删除 50 的倍数,则您当前的方法将不起作用,因为在第一次删除每个索引处的值是索引的不变量之后,不再成立。

modCount 是一个内部变量,ArrayList 使用它来检测它是否参考了它上面的任何迭代器而改变。它基本上计算了对列表的所有修改。Iterator 保持自己的计数,并检查它是否与 List 保持同步。

您的代码不会导致任何内存泄漏。如果仍然引用不再使用的对象,那么在 java 内存“泄漏”中,它们不能被垃圾收集。但是由于示例中的所有内容都在方法范围之外传递,因此一旦离开方法,所有内容都可以被 gc'd。(由于它是主要方法,vm 也会停止运行并释放它的内存)

于 2012-12-11T06:10:19.430 回答
3

列表不是数组,即使 an数组ArrayList支持。

intArray[i] = null;

不一样

arrayList.remove(i);

在第二个中(使用ArrayList),您实际上是i+1在向下移动所有元素,从而减小列表的大小。

如果您需要从正在执行迭代的列表中删除元素,您可以使用Iterator

Iterator<Integer> iterator = list.iterator();
int i = 0;
while (iterator.hasNext()) {
    iterator.next(); // consume current item
    if ((i++ % 50) == 0) {
        iterator.remove();
    }
}

或者你可以使用这个 hacky hack

for(int i=0, len=lst.size();i<len;i++){
    if((i%50)==0){
        lst.remove(i);
        len--;  // decrease size of upper bound check
    }           
}

// or better...
for (int len=lst.size() - 1, i=len - (len % 50); i>=0; i-=50){
   lst.remove(i);
}

...但迭代器解决方案是你应该如何处理这种情况通常如何通过一个Collection.

modCount每次您add或您的( )中的remove元素时都会增加。这很重要,因为当您迭代元素时,您不想“错过”或让其他东西在中途改变您的元素。(在多线程应用程序中尤其如此。)因此,如果某些进程在使用迭代器时修改了您的,您将收到警告,表明您的列表自迭代器创建以来已更改。(参见List.iterator()ListArrayListArrayListConcurrentModificationException

最后,对于您的最后一个问题,您不应该担心内存泄漏和ArrayList,除非您的列表中的元素在其他地方被引用。在 Java 中,只要一个对象没有被任何其他对象引用,它就成为垃圾回收的候选对象。

于 2012-12-11T06:17:13.387 回答
1

当您删除列表中的元素时,您的列表大小会变小。

于 2012-12-11T06:12:56.503 回答
1
 List<Integer> removeElements = new ArrayList<Integer>(); 
 for(int i=0;i<10000;i++){
            if((i%50)==0){
                removeElements.add(lst.get(i));
            }           
        }

 lst.removeAll(removeElements);

这是更安全的方法

关于内存泄漏:

在这里您不必担心内存泄漏,因为当引用卡住的时间超过所需时间并且没有收集垃圾时会发生内存泄漏。这通常是由于静态引用而发生的。

于 2012-12-11T06:14:40.793 回答
1

你可能想要这样做:

for(int i=0;i<10000;i++){
        if((i%50)==0){
            lst.remove(Integer.valueof(i));
        }           
    }

List 有 2 个删除方法,按索引和按对象。您的列表包含对象,您在其中添加了一些“int i”(原始),但编译器将其替换为自动装箱:

Integer.valueof(i)

因此,当您删除时,您不是按对象删除,而是按索引删除。

例如,您有列表:{3, 2, 1}

你打电话时:

  • remove(0), list become: {2, 1} // 按 id 删除
  • remove(1), list become: {3, 1} // 按 id 删除
  • remove(Integer.valueof(1)), list become: {3, 2} // 按对象删除
于 2012-12-11T06:15:50.953 回答
0

第一点:在这部分代码中,您将删除列表中的元素,请参见:

for(int i=0;i<10000;i++){
            if((i%50)==0){
                lst.remove(i);
            }           
        }

这就是为什么您的列表大小正在减少并且您收到该错误的原因,即java.lang.IndexOutOfBoundsException

第二点: modCount 看起来像 eclipse 的内部计数列表。第三点:随着列表大小的减少,这不会成为内存泄漏的原因。

希望这会帮助你。

于 2012-12-20T17:28:10.143 回答