遍历连接时出现 ConcurrentModificationException

ConcurrentModificationException when iterating through connections

我有一个模拟神经网络的程序,完成后将通过 NEAT 算法对其进行进化。

神经网络通过大量神经元工作,这些神经元通过连接连接起来。进化程序的一部分是跨越神经网络,基本上是从每个神经网络中获取随机神经元和连接并将它们放在一起。但是,如果它需要一个连接来连接 2 个不存在或禁用的神经元,它就会被禁用。如果一个神经元具有少于 1 个现有或启用的 input/output 连接,它将被禁用。但是,当遍历神经元的连接时,我得到 ConcurrentModificationException

我有点不明白为什么,我认为这是我的误会。代码有什么问题?

错误:

Exception in thread "AWT-EventQueue-0"    java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at GUIDisplay$Handler.actionPerformed(GUIDisplay.java:610)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access0(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.awt.EventQueue.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

GUIDisplay class(组织和展示神经网络):

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class GUIDisplay extends JPanel {

JButton save = new JButton("Save neural network");
JButton cross = new JButton("Cross 2 neural networks");
JButton newNN = new JButton("Create new neural network");
JButton load = new JButton("Load neural network");
NeuralRegister nr = new NeuralRegister();
int lastX = 0;
boolean drag = false;

InputNeuron inputA = new InputNeuron(nr),
        inputB = new InputNeuron(nr);

OutputNeuron outputA = new OutputNeuron(nr),
        outputB = new OutputNeuron(nr);

//ArrayList<Neuron> hidden = new ArrayList<Neuron>();
//ArrayList<Connection> connections = new ArrayList<Connection>();
ArrayList<JTextField> inputs = new ArrayList<JTextField>();
Map<String,NeuralRegister> species = new HashMap<String,NeuralRegister>();

Neuron startConnection;
Neuron endConnection;

JFrame j;

public GUIDisplay (final JFrame j) {

    this.j = j;

    setLayout(null);
    save.setLocation(10, 30);
    save.setSize(newNN.getPreferredSize());
    cross.setLocation(10, 55);
    cross.setSize(newNN.getPreferredSize());
    newNN.setLocation(10, 80);
    newNN.setSize(newNN.getPreferredSize());
    load.setLocation(10, 105);
    load.setSize(newNN.getPreferredSize());
    add(save);
    add(cross);
    add(newNN);
    add(load);

    for (Neuron n : nr.getNeurons()) {

        if (n instanceof InputNeuron) {

            JTextField text = new JTextField(Double.toString(n.getOutput()));
            text.setSize(100, 25);
            inputs.add(text);

        }

    }

    /*hidden.add(new Neuron(nr));
    hidden.add(new Neuron(nr));

    Connection inA = new Connection(inputA, hidden.get(0), nr);
    Connection inB = new Connection(inputB, hidden.get(1), nr);

    Connection outA = new Connection(hidden.get(hidden.size() - 1), outputA, nr);
    Connection outB = new Connection(hidden.get(hidden.size() - 2), outputB, nr);

    for (int i = 0; i < hidden.size(); i ++) {

        if (i < hidden.size() - 1) {

            Connection newC = new Connection(hidden.get(i), hidden.get(i + 1), nr);

        }

    }*/

    calculateNeuronLocations();

    inputA.setOutput(20);
    inputB.setOutput(-20);

    //inputA.calculateOutput();
    //inputB.calculateOutput();

    Handler h = new Handler();
    addMouseListener(h);
    addMouseMotionListener(h);
    save.addActionListener(h);
    cross.addActionListener(h);
    newNN.addActionListener(h);
    load.addActionListener(h);

}

public void calculateNeuronLocations () {

    int x = 100;
    int y = 250;

    int xGap = 100;
    int yGap = 40;

    for (Neuron n : nr.getNeurons()) {

        if (n instanceof InputNeuron) {

            n.setLocation(new Point(x,y));
            y += yGap;

        }

    }

    y = 250;
    x += xGap;

    for (Neuron n : nr.getNeurons()) {

        if (!(n instanceof InputNeuron) && !(n instanceof OutputNeuron)) {

            n.setLocation(new Point(x,y));
            y += yGap;

            if (y >= 200) {

                y = 100;
                x += xGap;

            }

        }

    }

    y = 250;
    x = j.getWidth() - 100;

    for (Neuron n : nr.getNeurons()) {

        if (n instanceof OutputNeuron) {

            n.setLocation(new Point(x,y));
            y += yGap;

        }

    }

    lastX = x;

}

@Override
public void paintComponent (Graphics g) {

    super.paintComponent(g);

    g.setColor(Color.BLACK);
    g.drawString("Connections: " + nr.getConnections().size(), 10, 10);
    g.drawString("Neurons: " + nr.getNeurons().size(), 10, 20);

    g.setColor(Color.BLUE);
    g.drawString("Input", 100, 230);
    g.drawString("Output", lastX, 230);

    for (Connection c : nr.getConnections()) {

        if (c.isEnabled()) {
            g.setColor(Color.GREEN);
        } else {
            g.setColor(Color.MAGENTA);
        }

        g.drawLine((int)c.getA().getLocation().getX(),
                (int)c.getA().getLocation().getY(),
                (int)c.getB().getLocation().getX(),
                (int)c.getB().getLocation().getY());

    }

    for (Neuron n : nr.getNeurons()) {

        if (n instanceof InputNeuron || n instanceof OutputNeuron) {

            g.setColor(new Color(150,0,0));

            g.drawRect((int)n.getLocation().getX() - 2,
                    (int)n.getLocation().getY() - 2,
                    4,
                    4);

        } else {

            if (n.isEnabled()) {
                g.setColor(Color.RED);
            } else {
                g.setColor(Color.BLUE);
            }

        }

        g.drawRect((int)n.getLocation().getX() - 5,
                (int)n.getLocation().getY() - 5,
                10,
                10);
        g.setColor(Color.BLACK);
        g.drawString(n.getId() + " (" + n.getOutput() + ")",
                (int)n.getLocation().getX() - 5,
                (int)n.getLocation().getY() - 5);

    }

    int cx = 100;
    int nx = 100;
    int y = j.getHeight() - 350;
    int width = 100, height = 100;

    for (Gene ge : nr.getGenome()) {

        boolean neuron = true;
        ArrayList<String> text = new ArrayList<String>();
        int yAddon = 15;
        int yStart = 15;

        text.add("ID: " + ge.getId());
        text.add("Enabled: " + ge.isEnabled());

        if (ge instanceof Neuron) {

            g.drawRect(nx, y, width, height);
            Neuron ne = (Neuron) ge;
            String nType = "Node: ";

            if (ne instanceof InputNeuron) {

                nType += "input";

            } else if (ne instanceof OutputNeuron) {

                nType += "output";

            } else {

                nType += "hidden";

            }

            text.add(nType);
            nx += width;

        } else if (ge instanceof Connection) {

            g.drawRect(cx, y + 150, width, height);
            yStart += 150;
            Connection ce = (Connection) ge;
            text.add("Neurons: " + ce.getA().getId() + " -> " + ce.getB().getId());
            text.add("Weight: " + ce.getWeight());
            cx += width;
            neuron = false;

        }

        for (String s : text) {

            int x = (neuron) ? nx : cx;
            g.drawString(s, x + 5 - 100, y + yStart + yAddon);
            yStart += yAddon;

        }

    }

    if (drag) {

        g.setColor(new Color(0,150,0));;
        if (startConnection != null) g.drawLine((int)startConnection.getLocation().getX(),
                (int)startConnection.getLocation().getY(),
                (int)getMousePosition().getX(),
                (int)getMousePosition().getY());

    }

    Set<String> keys = species.keySet();
    int x = j.getWidth() - 100;
    int yT = 30;
    int yGap = 15;

    for (String key : keys) {

        g.drawString(key, x, yT);
        yT += yGap;

    }

}

private class Handler implements ActionListener, MouseListener, MouseMotionListener {

    @Override
    public void mouseClicked(MouseEvent arg0) {

        Neuron toAdd = new Neuron(nr);
        toAdd.setLocation(getMousePosition());
        //hidden.add(toAdd);
        repaint();
        j.repaint();

    }

    @Override
    public void mouseEntered(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent arg0) {

        for (Neuron n : nr.getNeurons()) {

            Rectangle m = new Rectangle((int)getMousePosition().getX(),
                    (int)getMousePosition().getY(),
                    1,1);
            Rectangle r = new Rectangle();
            r.setLocation((int)n.getLocation().getX() - 5, (int)n.getLocation().getY() - 5);
            r.setSize(10, 10);
            if (m.intersects(r)) {

                startConnection = n;

            }

        }

    }

    @Override
    public void mouseReleased(MouseEvent arg0) {

        if (startConnection != null) {

        if (drag) {

            boolean found = false;

            for (Neuron n : nr.getNeurons()) {

                if (n != startConnection) {

                Rectangle m = new Rectangle((int)getMousePosition().getX(),
                        (int)getMousePosition().getY(),
                        1,1);
                Rectangle r = new Rectangle();
                r.setLocation((int)n.getLocation().getX() - 5, (int)n.getLocation().getY() - 5);
                r.setSize(10, 10);
                if (m.intersects(r)) {

                    endConnection = n;
                    found = true;

                }

                }

            }

            if (found) {

                Connection c = new Connection(startConnection,endConnection,nr);
                c.transferOutput(c.getA().getOutput());

            }

            repaint();
            j.repaint();

        }

        }

        drag = false;
        startConnection = null;

    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getSource() == save) {

            String name = JOptionPane.showInputDialog(null, "Enter desired name for network", "Save", JOptionPane.INFORMATION_MESSAGE);
            species.put(name, nr);
            repaint();

        } else if (e.getSource() == newNN) {

            nr = new NeuralRegister();
            InputNeuron inputA = new InputNeuron(nr),
                    inputB = new InputNeuron(nr);

            OutputNeuron outputA = new OutputNeuron(nr),
                    outputB = new OutputNeuron(nr);

            inputA.setOutput(20);
            inputB.setOutput(-20);

            calculateNeuronLocations();
            repaint();

        } else if (e.getSource() == load) {

            load();

        } else if (e.getSource() == cross) {

            String nr1Str = JOptionPane.showInputDialog(null,"Enter name of first neural network", "Cross", JOptionPane.INFORMATION_MESSAGE);
            String nr2Str = JOptionPane.showInputDialog(null,"Enter name of second neural network", "Cross", JOptionPane.INFORMATION_MESSAGE);
            NeuralRegister nr1 = species.get(nr1Str);
            NeuralRegister nr2 = species.get(nr2Str);
            NeuralRegister nrNew = new NeuralRegister();
            NeuralRegister nrAdded = new NeuralRegister();
            ArrayList<Neuron> nDone = new ArrayList<Neuron>();
            ArrayList<Connection> cDone = new ArrayList<Connection>();

            /*NeuralRegister biggestNeurons = (nr1.getNeurons().size() > nr2.getNeurons().size()) ? nr1 : nr2;
            NeuralRegister smallestNeurons = (biggestNeurons == nr1) ? nr2 : nr1;

            NeuralRegister biggestConnections = (nr1.getConnections().size() > nr2.getConnections().size()) ? nr1 : nr2;
            NeuralRegister smallestConnections = (biggestConnections == nr1) ? nr2 : nr1;
            */

            for (Neuron n : nr1.getNeurons()) {

                nrAdded.registerNeuron(n);

            }

            for (Neuron n : nr2.getNeurons()) {

                nrAdded.registerNeuron(n);

            }

            for (Connection c : nr1.getConnections()) {

                nrAdded.registerConnection(c);

            }

            for (Connection c : nr2.getConnections()) {

                nrAdded.registerConnection(c);

            }

            for (Neuron g1 : nrAdded.getNeurons()) {

                if (!nDone.contains(g1)) {

                Neuron match = null;
                ArrayList<Neuron> search = (nr1.getNeurons().contains(g1)) ? nr2.getNeurons() :  nr1.getNeurons();

                for (Neuron g2 : search) {

                    if (g1.getId() == g2.getId()) {

                        match = g2;
                        //System.out.println("Match Found " + g1.getId());
                        nDone.add(g2);

                    }

                }

                Random r = new Random();
                Neuron select = null;
                if (match != null) {

                    select = (r.nextInt(2) == 1) ? g1 : match;
                    //System.out.println("Selected " + ((select == g1) ? "NR1" : "NR2"));

                } else {

                    select = (r.nextInt(2) == 1) ? g1 : null;

                }

                nDone.add(g1);

                if (select != null) nrNew.registerNeuron(select);

                }

            }

            for (Connection c : nrAdded.getConnections()) {

                if (!cDone.contains(c)) {

                    Connection match = null;
                    ArrayList<Connection> search = (nr1.getConnections().contains(c)) ? nr2.getConnections() :  nr1.getConnections();

                    for (Connection g2 : search) {

                        if (c.getId() == g2.getId()) {

                            match = g2;
                            //System.out.println("Match Found " + g1.getId());
                            cDone.add(g2);

                        }

                    }

                    Random r = new Random();
                    Connection select = null;
                    if (match != null) {

                        select = (r.nextInt(2) == 1) ? c : match;
                        //System.out.println("Selected " + ((select == g1) ? "NR1" : "NR2"));

                    } else {

                        select = (r.nextInt(2) == 1) ? c : null;

                    }

                    cDone.add(c);

                    if (select != null) {

                    int complete = 0;

                    for (Neuron n : nrNew.getNeurons()) {

                        if (select.getA().getId() == n.getId() && n.isEnabled()) {

                            complete ++;
                            select.setA(n);

                        } else if (select.getB().getId() == n.getId() && n.isEnabled()) {

                            complete ++;
                            select.setB(n);

                        }

                    }

                    if (complete == 2) {

                        //select.setEnabled(true);
                        nrNew.registerConnection(select);

                    } else {

                        select.setEnabled(false);
                        nrNew.registerConnection(select);

                    }

                    }

                }

            }

            for (Neuron n : nrNew.getNeurons()) {

                for (Connection c : n.getInputs()) {

                    for (Connection c2 : nrNew.getConnections()) {

                        if (c.getId() != c2.getId() || !c2.isEnabled()) {

                            n.getInputs().remove(c);

                        }

                    }

                }

                //CONCURRENT MODIFICATION EXCEPTION
                for (Connection c : n.getOutputs()) {

                    for (Connection c2 : nrNew.getConnections()) {

                        if (c.getId() != c2.getId() || !c2.isEnabled()) {

                            n.getOutputs().remove(c);

                        }

                    }

                }

                if (n.getInputs().size() < 1 || n.getOutputs().size() < 1) {

                    if (!(n instanceof InputNeuron) &&
                            !(n instanceof OutputNeuron))
                        n.setEnabled(false);

                }

            }

            load(nrNew);

        }

    }

    @Override
    public void mouseDragged(MouseEvent arg0) {

        drag = true;
        repaint();

    }

    @Override
    public void mouseMoved(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

}

public void load () {

    String search = JOptionPane.showInputDialog(null,"Enter name of neural network to load", "Load", JOptionPane.INFORMATION_MESSAGE);
    load(species.get(search));
    repaint();

}

public void load (NeuralRegister nrLoad) {

    //String search = JOptionPane.showInputDialog(null,"Enter name of neural network to load", "Load", JOptionPane.INFORMATION_MESSAGE);
    nr = nrLoad;
    nr.refresh();
    repaint();

}

}

Neuron class:

import java.awt.Point;
import java.util.ArrayList;


public class Neuron extends Gene {

double threshold = 1;
ArrayList<Connection> inputs = new ArrayList<Connection>();
ArrayList<Connection> outputs = new ArrayList<Connection>();
double output;
Point location = new Point(0,0);

public Neuron (NeuralRegister nr) {

    id = nr.registerNeuron(this);

}

public void calculateOutput () {

    double sumOfInputs = 0;
    //System.out.println("Neuron " + id + " evaluation\n----------");

    for (Connection c : inputs) {

        sumOfInputs += c.getWeight() * c.getValue();

    }

    //System.out.println("Sum of inputs = " + sumOfInputs);
    sumOfInputs += -1 * threshold;
    //System.out.println("Sum of inputs - threshold = " + sumOfInputs);
    output = 1/(1 + Math.pow(Math.E, -sumOfInputs));
    //System.out.println("Raw output = " + output);
    output = (output == 0.5) ? 0.5 : Math.round(output);
    //System.out.println("Output = " + output + "\n----------------");

    for (Connection c : outputs) {

        if (c.isEnabled()) c.transferOutput(output);

    }

}

public void addInput (Connection c) {

    inputs.add(c);

}

public void addOutput (Connection c) {

    outputs.add(c);

}

public double getOutput() {
    return output;
}

public void setOutput(double output) {
    this.output = output;
    for (Connection c : outputs) {

        c.transferOutput(output);

    }
}

public double getThreshold() {
    return threshold;
}

public void setThreshold(double threshold) {
    this.threshold = threshold;
}

public Point getLocation() {
    return location;
}

public void setLocation(Point location) {
    this.location = location;
}

public ArrayList<Connection> getInputs() {
    return inputs;
}

public void setInputs(ArrayList<Connection> inputs) {
    this.inputs = inputs;
}

public ArrayList<Connection> getOutputs() {
    return outputs;
}

public void setOutputs(ArrayList<Connection> outputs) {
    this.outputs = outputs;
}

}

Connection class:

public class Connection extends Gene {

Neuron a;
Neuron b;
double weight = 1;
double value = 0;

public Connection (Neuron start, Neuron end, NeuralRegister nr) {

    id = nr.registerConnection(this);
    a = start;
    b = end;
    a.addOutput(this);
    b.addInput(this);

    System.out.println("New connection made between " + a.getId() + " and " + b.getId());

}

public void transferOutput (double output) {

    value = output;
    b.calculateOutput();

}

public void setWeight (double w) {

    weight = w;

}

public double getWeight () {

    return weight;

}

public double getValue() {
    return value;
}

public void setValue(double value) {
    this.value = value;
}

public Neuron getA() {
    return a;
}

public void setA(Neuron a) {
    this.a = a;
}

public Neuron getB() {
    return b;
}

public void setB(Neuron b) {
    this.b = b;
}

}
for (Connection c : n.getInputs()) {

    for (Connection c2 : nrNew.getConnections()) {

        if (c.getId() != c2.getId() || !c2.isEnabled()) {

            n.getInputs().remove(c);

        }

    }

}

//CONCURRENT MODIFICATION EXCEPTION
for (Connection c : n.getOutputs()) {

    for (Connection c2 : nrNew.getConnections()) {

        if (c.getId() != c2.getId() || !c2.isEnabled()) {

            n.getOutputs().remove(c);

        }

    }

}

您在循环访问集合且未使用迭代器时从 n.getInputs()n.getOutputs()Connection 集合中删除。所有类似问题的解决方案都是相同的:获取迭代器,用迭代器循环遍历集合,只用迭代器移除。

您不应该在迭代 ArrayList 时添加或删除项目。

//CONCURRENT MODIFICATION EXCEPTION
for (Connection c : n.getOutputs()) {

   for (Connection c2 : nrNew.getConnections()) {

       if (c.getId() != c2.getId() || !c2.isEnabled()) {

          n.getOutputs().remove(c);

       }

   }

}

在上面的代码中,您正在删除项目。不要那样做。将它们添加到另一个List中,然后在迭代结束后使用ArrayListremoveAll(itemsToRemove)方法,像这样:

List<Connection> toRemove = new ArrayList<Connection>();

for (Connection c : n.getOutputs()) {

   for (Connection c2 : nrNew.getConnections()) {

       if (c.getId() != c2.getId() || !c2.isEnabled()) {

          toRemove.add(c);

       }

   }

}
n.getOutputs().removeAll(toRemove);

您应该防止多个线程同时迭代和删除。尝试同步该代码。

            synchronized (this){
                //CONCURRENT MODIFICATION EXCEPTION
                for (Connection c : n.getOutputs()) {
                        for (Connection c2 : nrNew.getConnections()) {
                        if (c.getId() != c2.getId() || !c2.isEnabled()) {
                            n.getOutputs().remove(c);
                        }
                    }
                }           
            }