【问题标题】:IndexOutOfBoundsException: I don't see in my while loopIndexOutOfBoundsException:我在我的 while 循环中看不到
【发布时间】:2015-11-17 05:19:29
【问题描述】:

我一直被困在这个IndexOutOfBoundsException 上,我无法弄清楚,我不明白为什么它指向我的while 循环。关于做什么的任何建议?顺便说一句,方法是一个循环调度模拟器,它似乎与我的一个测试文件完美地工作,但不是另一个。

public void roundRobin2(ArrayList<Jobs> c, int startSize)
{   
    //int n = 0;
    double counter = 0;
    double compTime = 0;
    while(!c.isEmpty())
    {
        int i = 0;
        System.out.println(c);
        for(i = 0; i < c.size(); i++)
        {
            if((c.get(i).jobTime) >= 2)
            {
                c.get(i).jobTime -= 2;
                counter += 2;

                if((c.get(i).jobTime) == 0)
                {
                    compTime += counter;
                }
            }
            else
            {
                (c.get(i).jobTime) -= 1;        
                counter += 1;

                if((c.get(i).jobTime) == 0)
                {
                    compTime += counter;
                }
            }
            //System.out.print("-" + c.get(i).jobName + "-" + counter);
            //n++;
            //if(n%10 == 0)
            //{
                //System.out.println("\n");
            //}
        }
        for(i = 0; i < c.size(); i++)
        {
            while(!c.isEmpty() && (c.get(i).jobTime) == 0)
            {
                c.remove(i);
            }       
        }
    }
    System.out.println("\n\nAverage completion times: "+ compTime + "/" + startSize +" = " + ((compTime)/startSize));
}

异常堆栈跟踪:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 7, Size: 7
    at java.util.ArrayList.rangeCheck(Unknown Source)
    at java.util.ArrayList.get(Unknown Source)
    at cs431.edu.cpp.Main.roundRobin2(Main.java:143)

【问题讨论】:

  • 如果您共享导致错误的输入会容易得多
  • @bumpy 我不这么认为。当您遇到这种错误时,只有越界访问的行很重要。然后,您跟踪用于计算超出范围的索引的变量发生的所有情况,并找出它无效的原因。如果所有内容都以 Collection.size() 而不是整数文字表示,则它与输入无关
  • @Dici 如果您不知道输入是什么,您如何跟踪变量的变化?输入是指 ArrayList 中的作业。
  • 只有索引很重要,循环中的绑定条件才是我的意思
  • 很抱歉删除了您编辑的文本,但这并不像 Stack Overflow 这样的问答风格方面的问题。您可以通过接受正确答案并为好的答案投票(如果您有必要的声誉)来表示您的满意。

标签: java arraylist while-loop indexoutofboundsexception


【解决方案1】:

您正在同时删除正在迭代的集合中的项目,这非常糟糕,您永远不应该这样做。您可以使用具有remove 方法的Iterator 来执行此操作(但这很少有效率),或者制作您的收藏的临时副本。顺便说一下,循环可以更容易地实现。

此解决方案使用LinkedList,因为头部删除和末尾插入都是O(1),这样既高效又安全。我还压缩了你的一些逻辑作为奖励:

public void roundRobin2(List<Jobs> jobs, int startSize) {
    double counter = 0;
    double compTime = 0;

    Deque<Jobs> uncompletedJobs = new LinkedList<>(jobs);
    while (!uncompletedJobs.isEmpty()) {
        Job job = uncompletedJobs.pop();
        int taskTime = job.jobTime >= 2 ? 2 : 1;
        job.jobTime -= taskTime;
        counter += taskTime;
        if (job.jobTime == 0) {
            compTime += counter;
        } else {
             uncompletedJobs.addLast(job);
        }
    }
    jobs.clear();
    System.out.println("\n\nAverage completion times: "+ compTime + "/" + startSize +" = " + ((compTime)/startSize));
}

【讨论】:

  • 我想向您推荐一个更好的实施方案,但我并不完全理解您的意图。为什么需要从集合中删除项目?
  • 我同意通常不应该这样做,但我认为这不是这种特定情况下的问题,因为他使用了 for 循环,并且每次都会重新评估 c.size()。跨度>
  • 不是在方法结束的时候
  • @Andreas 代码的复杂性不是循环数。在列表中,调用remove 平均为O(n),因为如果它是链表则需要迭代列表,如果它是数组列表则需要移动元素。如您所见,包含remove 的循环是O(n^2)。相反,我的代码有一个循环执行两个操作:头部删除和末尾插入。 LinkedList 提供对其两端的恒定时间访问,因此这些操作都不需要遍历列表。因此,整体复杂度为O(n)
  • @Dici 对不起,我现在明白你的意思了。通过使用链表作为队列和使用 pop/addLast 的“移动到队列末尾”逻辑,删除元素的成本是 O(1),复制列表的前期成本很小。竖起大拇指! (或者更确切地说投票)。
【解决方案2】:

您的代码:

while(!c.isEmpty() && (c.get(i).jobTime) == 0)
{
    c.remove(i);
}

如果i 正在删除最后一个元素,但不会导致列表变空,则将失败。改用这个:

while (i < c.size() && c.get(i).jobTime == 0)
{
    c.remove(i);
}

更新

您的代码可以通过使用for (obj : list) 循环进行大量清理,您应该使用Iterator 删除元素。这是清理后的代码:

public static void roundRobin2(ArrayList<Jobs> c, int startSize)
{
    double counter = 0;
    double compTime = 0;
    while (! c.isEmpty())
    {
        System.out.println(c);
        for (Jobs jobs : c)
        {
            if (jobs.jobTime >= 2)
            {
                jobs.jobTime -= 2;
                counter += 2;
            }
            else
            {
                jobs.jobTime -= 1;
                counter += 1;
            }
            if (jobs.jobTime == 0)
            {
                compTime += counter;
            }
        }
        for (Iterator<Jobs> jobsIter = c.iterator(); jobsIter.hasNext(); )
        {
            Jobs jobs = jobsIter.next();
            if (jobs.jobTime == 0)
            {
                jobsIter.remove();
            }
        }
    }
    System.out.println("\n\nAverage completion times: " + compTime + "/" + startSize + " = " + (compTime / startSize));
}

【讨论】:

  • 它不会以这种方式失败,但是您正在向 OP 提供建议,以保留其本来就很糟糕的代码。一个好的答案应该是不赞成在集合中循环删除项目。
  • 感谢安德烈亚斯这个工作,就像我说它在迭代过程中删除,因为我希望它在工作完成后立即执行我想要它
  • @Dici 是的,我也想清理他的代码,所以我就这样做了,但我的回答完全正确,因为它回答了他的问题。仅此而已,但这是一个非常有效的答案,绝不是对其他任何事情的建议。
  • @Dici 这是你的个人哲学,我对此表示赞赏,你应该在自己的答案中这样做,但请不要投反对票,这意味着“这个答案没有用”,当答案是对问题的有效直接答案时。
  • @Dici 谢谢,请继续努力提供更好的答案,但提出问题的人可能不在乎(其他读者可能),所以请不要气馁 简单答案,只要他们是“正确的”,当然,只要他们是“正确的”。 ;-)
猜你喜欢
  • 1970-01-01
  • 2012-11-06
  • 1970-01-01
  • 2018-06-12
  • 2015-07-12
  • 2012-11-07
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
相关资源
最近更新 更多