需要一些关于java.util.list的说明

时间:2012-12-11 06:08:14

标签: java list memory-leaks

需要对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)

另外需要注意的是 enter image description here

然后我在代码i-e迭代第二循环中做了一次更改,直到5000只,它工作正常

enter image description here

问题 为什么要给IndexOutOfBoundsException?

那是什么modCoutn?

是否有任何此事成为记忆泄漏的理由如果是,如何解决?

提前致谢。

6 个答案:

答案 0 :(得分:16)

从列表中删除元素会使其变小。你的第二个循环运行到10000,但是当它到达那里时,列表将缩小到小于10000.

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

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

请注意,如果您的意图是删除50的倍数,那么您当前的方法将无效,因为在第一次删除不变量后,每个索引的值都是索引,不再成立。

modCount是一个内部变量,ArrayList用它来检测它是否在引用任何迭代器时被更改。它基本上计算对List的所有修改。迭代器保留自己的计数,并检查它是否与List保持同步。

您的代码没有导致任何内存泄漏。在java内存'泄漏'中,如果仍然引用不再使用的对象,则不能对它们进行垃圾回收。但是由于示例中的所有内容都超出了方法的范围,因此一旦离开方法,所有内容都可以被gc'd。 (因为它是主要方法,vm将停止运行并释放其内存)

答案 1 :(得分:3)

列表不是数组,即使 数组支持<{1}}。

ArrayList

不同
intArray[i] = null;

在第二个(使用arrayList.remove(i); )中,您实际上正在向下移动所有元素ArrayList,从而减​​少列表的大小。

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

i+1

或者你可以使用这个hacky hack

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

...但是迭代器解决方案是你应该如何解决这种情况通常是如何逐步完成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); }

每次CollectionmodCount addremove)中的元素时,List都会递增。这很重要,因为当你迭代你的元素时,你不想“错过”或者让其他东西在中途改变你的元素。 (在多线程应用程序中尤其如此。)因此,如果某个进程在使用迭代器时修改了ArrayList,那么在创建迭代器后,您将收到ConcurrentModificationException警告您的列表已更改。 (见清单。iterator()

最后,对于您的上一个问题,您不应该担心内存泄漏和ArrayList,除非您的列表中的元素被引用到其他地方。在Java中,只要对象未被任何其他对象引用,它就是garbage collection的候选对象。

答案 2 :(得分:1)

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

答案 3 :(得分: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);

这是更安全的方法

关于内存泄漏:

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

答案 4 :(得分:1)

你可能想这样做:

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

List有2个删除方法,索引和Object。 你的清单包含OBJECTS,你加入一些&#34; int i&#34; (原始),但编译器将其替换为自动装箱:

Integer.valueof(i)

因此,当您删除时,不是通过Object删除,而是通过索引删除。

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

致电时:

  • 删除(0),列表变为:{2,1} //按ID删除
  • 删除(1),列表变为:{3,1} //按ID删除
  • remove(Integer.valueof(1)),list变为:{3,2} //按对象删除

答案 5 :(得分:0)

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

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

这就是为什么你的列表大小减少了,你得到了错误i-e java.lang.IndexOutOfBoundsException

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

希望这会对你有所帮助。