首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >意外IndexOutOfBoundsException

意外IndexOutOfBoundsException
EN

Stack Overflow用户
提问于 2014-08-25 04:48:59
回答 3查看 224关注 0票数 1

最近,我被代码中的一个奇怪的错误完全搞糊涂了,到了极度沮丧的地步。最后,我在我的代码中放入了System.out.println();,直到我缩小了范围,以揭示最奇怪的结果。

代码语言:javascript
复制
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");
  }
}

以及产出:

代码语言:javascript
复制
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 objects

getInstanceListReference()返回一个ArrayList<GameObj>GameObj是我项目中表示游戏对象的超级超类之一。此外,运行时内部源代码不会修改getInstanceListReference()返回的getInstanceListReference()

在for循环之前执行了一些代码:

代码语言:javascript
复制
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中的整数,并利用它们删除实例列表中的索引。

我是不是在做一些我不知道会导致问题的不良行为?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-08-25 05:34:18

如果没有我们自己可以运行的SSCCE,我们就不能给您一个肯定的答案。然而,根据您发布的描述和评论,这里有一个可能的解释:

您正在同时修改正在迭代的列表,其方式不能触发ConcurrentModificationException。尽管名称如此,但即使在单个线程中也完全可能遇到此类问题;下面是一个简单的示例(如果您愿意,可以使用SSCCE ):

代码语言:javascript
复制
int len = ls.size();
for(int i = 0; i < len; i++) {
  if(i %2 == 0) {
    ls.remove(i);
  }
}

当它在迭代时修改列表时,i就会与列表中的元素断开连接。而不是删除偶数元素,这可能是预期的行为,这将删除第0,第3,第6等,直到它IndexOutOfBoundException的一半的循环。

通常,避免此问题的正确方法是迭代一个集合并修改副本,或者只在删除操作期间使用Iterator。例如,这是删除所有其他元素的一种安全方法--注意,我们只使用迭代器,不管删除了什么元素,迭代器在列表中的位置都是保持不变的,而不是处理指示符:

代码语言:javascript
复制
Iterator<?> iter = ls.iterator();
while(iter.hasNext()) {
  iter.next();
  iter.remove();
  if(iter.hasNext()) {
    iter.next();
  }
}

好的,让我们尝试另一个例子,更直接地基于您循环中的代码。

我有一份我想从另一份名单中删除的起诉书:

代码语言:javascript
复制
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(),列表缩小了一个,列表后面的所有元素都向下移动了一个。

代码语言:javascript
复制
for(int i : toRemove) {
  myList.remove(i);
}

虽然我不能确切地说什么适合您的用例,但有一种选择是保留原始列表,修改副本,然后在所有更改完成后存储已修改的副本。有一种方法可以做到:

代码语言:javascript
复制
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);

我们不逐个删除项,而是检查是否应该保留每个索引,并跳过任何不应该保留的索引。这避免了尝试从正在缩小的列表中删除项目的不一致状态。

一般来说,随着项目规模的增加,使用可变集合可能很棘手。优秀的番石榴库提供了一组专门帮助人们避免这类问题的不变集合。对于解决问题,它需要一种稍微不同的思维方式,但这是一种更加健壮的方法。即使不使用番石榴,只要有可能,也要考虑集合不可变,并在尝试结构修改时复制防御性副本。

票数 5
EN

Stack Overflow用户

发布于 2014-08-25 06:32:21

我将重写accept(),以使用可用于处理集合的Java库方法。

  • 您的方法accept()被传递给表示要从DeathWith.getInstanceListReference()中删除的索引的Collection<Integer>
  • 您目前正在处理在迭代集合时修改集合的问题。

考虑使用Collections.removeAll()来完成你所需要的(我相信)。

代码语言:javascript
复制
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);
}

这假定:

  • nullDeathWish.getInstanceListReference()中的无效值。
票数 1
EN

Stack Overflow用户

发布于 2014-08-25 05:02:56

IndexOutOfBoundsException来自您的list.get(i),我假设它只有两个元素,而不是来自DeathWish.getInstanceListReference()列表!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25479195

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档