最近,我被代码中的一个奇怪的错误完全搞糊涂了,到了极度沮丧的地步。最后,我在我的代码中放入了System.out.println();,直到我缩小了范围,以揭示最奇怪的结果。
for (Iterator<Integer> it = list.iterator(); it.hasNext();) {
i = it.next();
System.out.println(DeathWish.getInstanceListReference()
.get(DeathWish.getInstanceListReference().size() -1) );
System.out.println("Golden number: " +
(DeathWish.getInstanceListReference().size() -1) );
System.out.println((DeathWish.getInstanceListReference().size() -1) == i);
System.out.println("CurrentInstance List: " +
Arrays.toString(DeathWish.getInstanceListReference().toArray()));
System.out.println("Iteration: " + i);
try {
System.out.println( DeathWish.getInstanceListReference()
.remove((int)list.get(i))); //remove unwanted objects from game paint list
} catch(IndexOutOfBoundsException ex) {
System.err.println(ex + " Error with multiple GameObj objects");
} finally {
System.out.println("\n");
}
}以及产出:
DeathWish.Bullet@ddd5de
Golden number: 2
true
CurrentInstance List: [DeathWish.Player@3a5cf7, DeathWish.Bullet@6cca54, DeathWish.Bullet@ddd5de]
Iteration: 2
java.lang.IndexOutOfBoundsException: Index: 2, Size: 2 Error with multiple GameObj objectsgetInstanceListReference()返回一个ArrayList<GameObj>。GameObj是我项目中表示游戏对象的超级超类之一。此外,运行时内部源代码不会修改getInstanceListReference()返回的getInstanceListReference()。
在for循环之前执行了一些代码:
System.out.println("\n------------------------\n" + "Read Instance List: " + Arrays.toString(tmpLock.toArray()) + "|" + Arrays.toString(tmpLock1.toArray()));
System.out.println("Recycle Bin: " + recycleBin.get(0) + "|" + recycleBin.get(1) + "\n------------------------\n");最上面的源代码中的这个列表指的是recycleBin。它保存预定要删除的对象的索引。使用IndexOutOfBoundsException的for循环正在循环recycleBin中的整数,并利用它们删除实例列表中的索引。
我是不是在做一些我不知道会导致问题的不良行为?
发布于 2014-08-25 05:34:18
如果没有我们自己可以运行的SSCCE,我们就不能给您一个肯定的答案。然而,根据您发布的描述和评论,这里有一个可能的解释:
您正在同时修改正在迭代的列表,其方式不能触发ConcurrentModificationException。尽管名称如此,但即使在单个线程中也完全可能遇到此类问题;下面是一个简单的示例(如果您愿意,可以使用SSCCE ):
int len = ls.size();
for(int i = 0; i < len; i++) {
if(i %2 == 0) {
ls.remove(i);
}
}当它在迭代时修改列表时,i就会与列表中的元素断开连接。而不是删除偶数元素,这可能是预期的行为,这将删除第0,第3,第6等,直到它IndexOutOfBoundException的一半的循环。
通常,避免此问题的正确方法是迭代一个集合并修改副本,或者只在删除操作期间使用Iterator。例如,这是删除所有其他元素的一种安全方法--注意,我们只使用迭代器,不管删除了什么元素,迭代器在列表中的位置都是保持不变的,而不是处理指示符:
Iterator<?> iter = ls.iterator();
while(iter.hasNext()) {
iter.next();
iter.remove();
if(iter.hasNext()) {
iter.next();
}
}好的,让我们尝试另一个例子,更直接地基于您循环中的代码。
我有一份我想从另一份名单中删除的起诉书:
List<Integer> toRemove = Arrays.asList(0,2,4,6);
List<String> myList = new ArrayList<>(
Arrays.asList("A","B","C","D","E","F","G","H"));现在,如果我试图从myList中删除0、2、4和6,我将得到一个IndexOutOfBoundsException,因为我称之为.remove(),列表缩小了一个,列表后面的所有元素都向下移动了一个。
for(int i : toRemove) {
myList.remove(i);
}虽然我不能确切地说什么适合您的用例,但有一种选择是保留原始列表,修改副本,然后在所有更改完成后存储已修改的副本。有一种方法可以做到:
Set<Integer> toRemoveSet = new HashSet<>(toRemove);
List<String> cleanList = new ArrayList<>();
for(int i = 0; i < myList.size(); i++) {
if(!toRemoveSet.contains(i)) {
cleanList.add(myList.get(i));
}
}
System.out.println(cleanList);我们不逐个删除项,而是检查是否应该保留每个索引,并跳过任何不应该保留的索引。这避免了尝试从正在缩小的列表中删除项目的不一致状态。
一般来说,随着项目规模的增加,使用可变集合可能很棘手。优秀的番石榴库提供了一组专门帮助人们避免这类问题的不变集合。对于解决问题,它需要一种稍微不同的思维方式,但这是一种更加健壮的方法。即使不使用番石榴,只要有可能,也要考虑集合不可变,并在尝试结构修改时复制防御性副本。
发布于 2014-08-25 06:32:21
我将重写accept(),以使用可用于处理集合的Java库方法。
accept()被传递给表示要从DeathWith.getInstanceListReference()中删除的索引的Collection<Integer>。考虑使用Collections.removeAll()来完成你所需要的(我相信)。
public void accept(Set<Integer> deadIndices) {
for (int i : deadIndices) {
DeathWish.getInstanceListReference().set(i, null);
}
List<GameObj> nullList = new ArrayList<GameObj>();
nullList.add(null);
DeathWish.getInstanceListReference().removeAll(nullList);
}这假定:
null是DeathWish.getInstanceListReference()中的无效值。发布于 2014-08-25 05:02:56
IndexOutOfBoundsException来自您的list.get(i),我假设它只有两个元素,而不是来自DeathWish.getInstanceListReference()列表!
https://stackoverflow.com/questions/25479195
复制相似问题