不可能的IndexOutOfBound发生在ArrayList.get()上。任何线索?

时间:2011-10-13 10:06:52

标签: java

我有一个非常奇怪的错误,想和你分享。

我有以下代码(简化):

public ArrayList<String> al = new ArrayList<String>();
public void doSomething() {
  int size = al.size();
  for(int i=0; i<size; i++) {
    if (al.get(i) != null) {
      System.out.println(al.get(i));
      String sPath = al.get(i);
      File fFile = new File(sPath);
      fFile.delete(); // Simplified. It has some error checking
    }
  }
}

我在生产环境中看到了一个错误:

java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
    at java.util.ArrayList.RangeCheck(Unknown Source)
    at java.util.ArrayList.get(Unknown Source)
    at MYCLASS.soSomething(MICLASS.java:1944)
    [...]

第1944行是if (al.get(i) != null) {

什么!怎么能提高IndexOutOfBound?!

问题是错误无法重现。我已经能够在开发环境中只提升一次,但是尝试重现它是不可能的(它不会再次提升)...所以无法在错误中寻找模式。

所以我唯一的选择很简单:阅读代码并使用大脑。

所以我浏览java.util.ArrayList.get()的代码:

public class ArrayList<E> [...]{
  public E get(int index) {
    RangeCheck(index);
    return (E) elementData[index];
  }
  private void RangeCheck(int index) {
    if (index >= size)
      throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
  }
}

所以它抛出了异常,因为index >= size ......怎么可能? for()0扫到size-1 ......这是不可能的!它无法引发错误!

等一下!此代码不是线程安全的。也许另一个线程在ArrayList上调用clear()remove*() ...研究代码,这是不可能的。所以我运行它,在1944行设置断点并在那一刻观察线程,实际上,其他正在运行的线程与此问题无关。

还有其他线索吗?

Kernnigan&amp;派克:“调试[...]不可能发生的事情,唯一可靠的信息就是确实发生了。”

6 个答案:

答案 0 :(得分:2)

我看到以下候选人如何发生这种情况:

  • 循环中的某些内容会更改i
  • 的值
  • 循环中的某些内容会更改al的大小,可能会删除元素。
  • 有些东西正在用不同的系列替换你的系列。
  • 我见过这样一种情况,即循环是以某种方式构造的,所以在空集合的情况下,身体甚至被执行一次。虽然这对于所描述的for循环似乎不太可能。

调试此问题的想法:将列表替换为您自己的实现,该实现记录对它的每次访问,并将所有实际功能委派给标准实现。

如果需要,它可以打印新创建的异常的堆栈跟踪,以便识别它的调用位置。当从不同的线程访问它时,它甚至可以抛出异常。

答案 1 :(得分:1)

每个人都在建议你从数组中删除元素。问题的解决方案是使用Iterator,它允许您遍历整个集合,但仍允许您修改该集合。

答案 2 :(得分:0)

您可能会从for循环中的ArrayList中删除元素,因此会减小用于退出此循环的原始大小!

答案 3 :(得分:0)

您对简化代码是否完全复制生产代码有多确定?在我看来,你的简化代码不可能引发IndexOutOfBoundsException因为列表是空的,所以永远不应该处理for循环。您是否在循环中修改数组列表的内容?

答案 4 :(得分:0)

另一种可能是另一个线程正在修改列表,而上面的代码正在扫描它。

如果你想要比猜测更好的东西,你需要向我们展示相关的生产代码,或者创建一个在运行时实际表现出相同问题的简化版本。


  

研究代码,这是不可能的。

显然 可能,否则你就不会观察到它。

答案 5 :(得分:0)

感谢大家的回答。大多数建议都基于:

A)循环正在修改ali

B)另一个线程正在修改al

c)另一个帖子是替换 al

由于我知道A不是这种情况,我敢打赌,在某些情况下,另一个线程正在修改/替换al。由于我无法在模式中使其失败,因此这种线程可能仅在某些情况下运行,例如错误条件。虽然循环中的代码非常简单,但是应用程序并不是很小,滥用单例和getter / setter,并且它会加强调试,所以我不会对此感到惊讶。

这与我怀疑的相同(除了C,我没有),但你确认这是唯一可能的原因。谢谢你的帮助。