不确定ConcurrentModificationException的原因

时间:2009-12-22 12:43:44

标签: java collections concurrency

这是我在Locale l中构建可能的城市之旅的代码(这不是最佳选择,只是为了让我的AI搜索先行一步)。

我得到一个ConcurrentModificationException,据我所知,当多个代码访问变量/集合并尝试修改它时,就会发生这种情况。导致此代码变得不快乐:

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

我修改它,因为我正在添加一个元素,但由于Iterator没有添加(只删除)的方法,我正在使用该集合的方法。

所以,我的问题是:

  1. 我是否添加了导致问题的元素?
  2. 如果是,请如何正确添加,以便modCount正确且我没有ConcurrentModificationException
  3. 下面的完整方法,对ConcurrentModificationException发生的行进行评论:

    public void construct() {
        tour = new ArrayList();
        ArrayList<City> lcl = new ArrayList(l.getCitys());
    
        tour.add(lcl.remove(0));
        tour.add(lcl.remove(1));
    
        while (!this.tourComplete()) {
            System.out.println(tour.size());
            Iterator tourit = tour.iterator();
            City g1 = (City) tourit.next();
            City g2 = (City) tour.get(lcl.indexOf(g1)+1);
    
            int gapDist = l.distanceBetweenCitys(g1, g2);
    
            while (tourit.hasNext()) {
                City C = null;
                int best = Integer.MAX_VALUE;
    
                for (Iterator lclit = lcl.iterator(); lclit.hasNext(); ) {
                    City c = (City) lclit.next();
                    int avg = (l.distanceBetweenCitys(g1,c) + 
                               l.distanceBetweenCitys(g2, c))/2 ;
    
                    if ( (avg<gapDist) && (avg<best) ) {
                        C = c;
                        best = avg;
                    }
                }
    
                if (C != null) {
                    assert(best == Integer.MAX_VALUE);
                    City A = tour.get(0);
                    City Z = tour.get(tour.size()-1);
    
                    boolean begin = true;
    
                    for (Iterator lclit = lcl.iterator();   lclit.hasNext(); ) {
                        City c = (City) lclit.next();
                        int dist = l.distanceBetweenCitys(A,c);
    
                        if ( dist<best ) {
                            begin = true;
                            C = c;
                            best = dist;
                        }
                    }
    
                    for (Iterator lclit = lcl.iterator();   lclit.hasNext(); ) {
                        City c = (City) lclit.next();
                        int dist = l.distanceBetweenCitys(Z,c);
    
                        if ( dist<best ) {
                            begin = false;
                            C = c;
                            best = dist;
                        }
                    }
    
                    if (begin) {
                        // one of these is causing the problem
                        tour.add(0,C);
                    }
                    else {
                        // one of these is causing the problem
                        tour.add(C);
                    }
                }
                else {
                    // one of these is causing the problem
                    tour.add(tour.indexOf(g2),C);
                }
    
                g1 = (City) tourit.next(); // this is where it all goes wrong 
                g2 = (City) tour.get(lcl.indexOf(g1)+1);
                gapDist = l.distanceBetweenCitys(g1, g2);
            }
        }
    }
    

5 个答案:

答案 0 :(得分:5)

使用迭代器时不能修改底层集合(通过迭代器本身除外)。

我没有通过你的算法(你似乎想在任意位置插入,这可能很棘手),但也许你可以做以下其中一种:

  1. 收集您要在第二个集合中添加的所有内容,并在完成后执行addAll

  2. 改为对集合的副本进行迭代。

  3. 使用ListIteratoradd外还有remove方法。

  4. 根本不使用迭代器,只需按索引访问ArrayList(无论如何你已经在其他地方使用过)

  5. 此外,您可以通过指定迭代器的类型(与您对列表的操作相同)来消除大量的类型转换。

答案 1 :(得分:1)

在Iterator循环内部,您尝试修改列表
摘自你的代码

if (begin) {
                    // one of these is causing the problem
                    tour.add(0,C);
                }
                else {
                    // one of these is causing the problem
                    tour.add(C);
                }

这是不允许的。适用于javaDoc http://java.sun.com/j2se/1.4.2/docs/api/java/util/ConcurrentModificationException.html

  

可能会抛出此异常   检测到并发的方法   在这样的情况下修改对象   修改是不允许的。对于   例如,通常不是   permssible一个线程修改一个   收集而另一个线程是   迭代它。一般来说,   迭代的结果是未定义的   在这些情况下。一些   迭代器实现(包括   所有收藏品   JRE提供的实现)   如果,可以选择抛出此异常   检测到此行为。迭代器   这样做被称为失败快速   迭代器,因为它们很快就会失败   干净利落,而不是冒险   任意的,非确定性的行为   在未来的未定时间。

答案 2 :(得分:1)

您可以使用索引变量而不是迭代器轻松解决此问题:

int i = 0;
while (i < tour.size()) {
    ....
    i++;
}

但是,您会注意到将元素插入到您正在迭代的列表中会引发一些棘手的问题。 Iterator抛出ConcurrentModificationException是有原因的,因为继续迭代的逻辑没有很好地定义。如果在索引位置之前插入元素,则索引不再指向相同的“当前”元素,并且需要将索引增加2以查找下一个元素。如果你插入之后,除了停止条件之外什么都没有改变(但是tour.size()会正确增长,所以没关系)。如果你在不同的位置进行多次插入/删除,那么很难跟踪......

我有一种感觉,你的算法也会被简化,虽然我不清楚它应该做什么。

答案 3 :(得分:0)

来自ArrayList的javadoc

  

此类的迭代器和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出一个ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒着任意的,非确定性行为的风险。

答案 4 :(得分:0)

这是我的丑陋但现在正在运行的代码,对于任何想要了解我最终如何获得它的人来说,我打算对它进行相当多的介绍但是我很满意它的工作原理

    public void construct()
{
tour = new ArrayList();
ArrayList<City> lcl = new ArrayList(l.getCitys());

tour.add(lcl.remove(0));
tour.add(lcl.remove(1));
tour.add(lcl.remove(2));

while (!this.tourComplete())
{

System.out.println(tour.size());
ListIterator<City> tourit = tour.listIterator();
City g1 = (City) tourit.next();
City g2 = (City) tour.get(tour.indexOf(g1)+1);

int gapDist = l.distanceBetweenCitys(g1, g2);

    while ((tourit.nextIndex()!=tour.size()-1) && !this.tourComplete())
    {
    System.out.println("x");
    City C = null;
    int best = Integer.MAX_VALUE;

        for (ListIterator<City> lclit = lcl.listIterator();   lclit.hasNext(); )
        {
        City c = (City) lclit.next();
        int avg = (l.distanceBetweenCitys(g1,c)+ l.distanceBetweenCitys(g2, c))/2 ;

        if ( (avg<gapDist) && (avg<best) )
        {
        C=c;
        best=avg;
        }

        }

    if (C==null)
    {
    System.out.println("C null");
        assert(best==Integer.MAX_VALUE);
        City A = tour.get(0);
        City Z = tour.get(tour.size()-1);

        boolean begin = true;

        for (ListIterator<City> lclit = lcl.listIterator();   lclit.hasNext(); )
        {
            City c = (City) lclit.next();
            int dist = l.distanceBetweenCitys(A,c);

            if ( dist<best )
            {
            begin=true;
            C=c;
            best=dist;
            }

        }

        for (ListIterator<City> lclit = lcl.listIterator();   lclit.hasNext(); )
        {
            City c = (City) lclit.next();
            int dist = l.distanceBetweenCitys(Z,c);

            if ( dist<best )
            {
            begin=false;
            C=c;
            best=dist;
            }

        }

        if(begin)
        {
        System.out.println("add at begining");
        System.out.println(this.TourtoString());
         // add in at 0

            int itpos = tourit.nextIndex();
            //move iterator to 0
            while (tourit.hasPrevious())
            {
            tourit.previous();
            }

            lcl.remove(C);
            // add in C
            tourit.add(C);

            // move iterator back
            while(tourit.nextIndex()!=(itpos+1))
            {
            tourit.next();
            }
        System.out.println(this.TourtoString());
        }
        else
        {
         // add in at end
            int itpos = tourit.nextIndex();
            //move iterator to end
            while (tourit.hasNext())
            {
            tourit.next();
            }

            lcl.remove(C);
            // add in C
            tourit.add(C);

            // move iterator back
            while(tourit.nextIndex()!=itpos)
            {
            tourit.previous();
            }
        }

    }
    else
    {
    System.out.println("C not null");

    // add in at g2
    int moveto = tour.indexOf(g2);
    int itpos = tourit.nextIndex();

    System.out.println("add at: "+ moveto);
    System.out.println(this.TourtoString());


    if (itpos>=moveto)
    {
    itpos++;
    }

    //move iterator to 0
    while (tourit.hasPrevious())
    {
    tourit.previous();
    }

    // move iterator to insertion location
    while (tourit.nextIndex()!=moveto)
    {
    tourit.next();
    }

    lcl.remove(C);
    // add in C
    tourit.add(C);

    //move iterator to 0
    while (tourit.hasPrevious())
    {
    tourit.previous();
    }


    // move iterator back
    while(tourit.nextIndex()!=itpos)
    {
    tourit.next();
    }

     System.out.println(this.TourtoString());
    }


    g1 = (City) tourit.next(); 
    g2 = (City) tour.get(tour.indexOf(g1)+1);
    gapDist = l.distanceBetweenCitys(g1, g2);
    }

}

}