Java ConcurrentModificationException;单线程;没有为每个循环

时间:2016-09-17 15:44:26

标签: java

我目前正在编写一个BTree算法。 我已经实现了向其添加Numbers的功能。当我去测试时,我遇到了ConcurrentModificationException。

我研究了这个问题并发现了一些提示,主要是由于两件事:

  1. 多个线程访问同一个列表。
  2. 使用for each循环修改List。
  3. 我的代码中都没有出现这些问题,所以我完全迷失了......

    我希望你们能帮助我!

    “Knoten”类的代码:

    public List<Nodes> Childnodes;
    public List<Integer> Keys;
    
    private Baum tree;
    private Nodes parent;
    private boolean isRoot;
    
    
    public void Add(int number)
    {
        int lastSmallerPosition = -1;
    
        for(int i = 0; i < Keys.size(); i++)
        {
            if(Keys.get(i) < number)
            {
                lastSmallerPosition = i;
            }
            else
            {
                if( Keys.size() < i + 1 && number == Keys.get(i + 1))
                {
                    return; 
                }
                break;
            }
        }
    
        if(isLeaf())
        {
            Keys.add(lastSmallerPosition + 1, number);
    
            if(Keys.size() > tree.maxKeys)
            {
                if(isRoot) 
                {
                    Nodes k1 = new Nodes(tree, this, false);
                    Nodes k2 = new Nodes(tree, this, false);
                    k1.Keys.addAll(Keys.subList(0, Keys.size()/2));
                    k2.Keys.addAll(Keys.subList(Keys.size()/2 + 1, Keys.size()));
                    Childnodes.add(k1);
                    Childnodes.add(k2);
    
                    int spareNumber = Keys.get(Keys.size()/2);
                    Keys.clear();
                    Keys.add(spareNumber);
                }
                else
                {
                    if(parent.Keys.size() == tree.maxKeys)
                    {
                        List<Integer> list = new ArrayList<Integer>();
    
                        if(parent.Childnodes.indexOf(this) == 0)
                        {
    
                            int index = parent.Childnodes.indexOf(this);
    
                            list.addAll(Keys);
                            list.add(parent.Keys.get(index));
                            list.addAll(parent.Childnodes.get(index + 1).Keys);
    
                            Keys.clear();
                            parent.Keys.remove(index);
                            parent.Childnodes.remove(index + 1);
                        }
                        else
                        {
    
                            int index = parent.Childnodes.indexOf(this) - 1; 
    
                            list.addAll(parent.Childnodes.get(index).Keys);
                            list.add(parent.Keys.get(index));
                            list.addAll(Keys);
    
                            parent.Childnodes.remove(index);
                            parent.Keys.remove(index);
                            Keys.clear();
                        }
    
                        int keysPerChildnode = (list.size() - tree.minKeys) / tree.minChilds;
                        int extraKeys = (list.size() - tree.minKeys) % tree.minChilds; 
                        int usedExtraKeys = 0;
    
                        for(int i = 0; i < tree.minKeys - 1; i ++)
                        {
                            Childnodes.add(new Nodes(tree, this, false));
                            Childnodes.get(i).Keys.addAll(
                                    list.subList(i * keysPerChildnode + usedExtraKeys + i, (i + 1) * keysPerChildnode + usedExtraKeys + i));
                            if(usedExtraKeys < ExtraKeys)
                            {
                                Childnodes.get(i).Keys.add(list.get((i + 1) * keysPerChildnode + usedExtraKeys + i));
                                usedExtraKeys++;
                            }
    
                            Keys.add(list.get((i + 1) * keysPerChildnode + usedExtraKeys + i));
                        }
    
                        Childnodes.add(new Nodes(tree, this, false));
                        Childnodes.get(tree.minChilds - 1).Keys.addAll(list.subList(list.size() - keysPerChildnode, list.size()));
                    }
                    else
                    {
                        Nodes k = new Nodes(tree,parent,false);
                        k.Keys = Keys.subList(Keys.size() / 2, Keys.size());
                        int NewParentKey  = Keys.get(Keys.size() / 2 - 1);
    
                        Keys = Keys.subList(0, Keys.size() / 2 - 1);
    
                        int index = parent.Childnodes.indexOf(this);
    
                        parent.Keys.add(index, NewParentKey);
                        parent.Childnodes.add(index + 1, k);
                    }
                }
            }
        }
        else 
        {
            Childnodes.get(lastSmallerPosition + 1).Add(number);
        }
    }
    

    例外:

    java.util.ConcurrentModificationException
        at java.util.ArrayList$SubList.checkForComodification(Unknown Source)
        at java.util.ArrayList$SubList.size(Unknown Source)
        at Baum.Knoten.Add(Knoten.java:39)
        at Baum.Knoten.Add(Knoten.java:175)
        at Baum.Baum.Add(Baum.java:31)
        at Test.BaumTest.AddTest(BaumTest.java:47)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
    

1 个答案:

答案 0 :(得分:1)

阅读List.subList(int fromIndex, int toIndex)的javadoc:

  

返回指定fromIndex(包含)和toIndex(不包括)之间此列表部分的视图。 (如果fromIndex和toIndex相等,则返回的列表为空。)返回的列表由此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然。返回的列表支持所有可选的列表操作。

     

[...]

     

如果支持列表(即此列表)以结构修改 以除了返回列表之外的任何方式,此方法返回的列表的语义将变为未定义。 (结构修改是那些改变了这个列表的大小,或以其他方式扰乱它的方式,正在进行的迭代可能会产生不正确的结果。)

如果您希望将子列表与原始列表断开连接,则需要复制该子列表:

new ArrayList<>(list.subList(from, to))