我有一个非常奇怪的错误,想和你分享。
我有以下代码(简化):
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;派克:“调试[...]不可能发生的事情,唯一可靠的信息就是确实发生了。”
答案 0 :(得分:2)
我看到以下候选人如何发生这种情况:
i
。al
的大小,可能会删除元素。调试此问题的想法:将列表替换为您自己的实现,该实现记录对它的每次访问,并将所有实际功能委派给标准实现。
如果需要,它可以打印新创建的异常的堆栈跟踪,以便识别它的调用位置。当从不同的线程访问它时,它甚至可以抛出异常。
答案 1 :(得分:1)
每个人都在建议你从数组中删除元素。问题的解决方案是使用Iterator
,它允许您遍历整个集合,但仍允许您修改该集合。
答案 2 :(得分:0)
您可能会从for循环中的ArrayList
中删除元素,因此会减小用于退出此循环的原始大小!
答案 3 :(得分:0)
您对简化代码是否完全复制生产代码有多确定?在我看来,你的简化代码不可能引发IndexOutOfBoundsException
因为列表是空的,所以永远不应该处理for循环。您是否在循环中修改数组列表的内容?
答案 4 :(得分:0)
另一种可能是另一个线程正在修改列表,而上面的代码正在扫描它。
如果你想要比猜测更好的东西,你需要向我们展示相关的生产代码,或者创建一个在运行时实际表现出相同问题的简化版本。
研究代码,这是不可能的。
显然 可能,否则你就不会观察到它。
答案 5 :(得分:0)
感谢大家的回答。大多数建议都基于:
A)循环正在修改al
或i
。
B)另一个线程正在修改al
。
c)另一个帖子是替换 al
。
由于我知道A不是这种情况,我敢打赌,在某些情况下,另一个线程正在修改/替换al
。由于我无法在模式中使其失败,因此这种线程可能仅在某些情况下运行,例如错误条件。虽然循环中的代码非常简单,但是应用程序并不是很小,滥用单例和getter / setter,并且它会加强调试,所以我不会对此感到惊讶。
这与我怀疑的相同(除了C,我没有),但你确认这是唯一可能的原因。谢谢你的帮助。