java.util.ConcurrentModificationException由于意外修改

时间:2019-03-19 08:26:58

标签: java

我正在实施A *算法,以将给定数量的出租车分配给给定数量的客户:

public SearchNode AStarSearch(List<Car> allCars, List<Customer> allCustomers) {

    // creates first node
    SearchNode firstNode = createFirstNode(allCars, allCustomers);
    open.add(firstNode);

    SearchNode currentNode;

    while(true) {
        currentNode = open.poll();   //error thrown here
        if (currentNode.allCustomers.isEmpty()) break;

        closed.add(currentNode);

        SearchNode temp;

        Iterator<Customer> itrCus = currentNode.allCustomers.iterator();
        Iterator<Car> itrCar = currentNode.allCars.iterator();

        while (itrCus.hasNext()) {
            Customer currentCustomer = itrCus.next();
            while (itrCar.hasNext()) {
                Car currentCar = itrCar.next();
                temp = new SearchNode(currentNode, currentCar, currentCustomer);
                openUpdate(currentNode, temp);
            }
        }
    }
    return currentNode;
}

现在对于孩子SearchNode,我要删除该客户已服务的客户。但这只能在搜索节点内部发生。

这是SearchNode类中发生的事情

public class SearchNode implements Comparable<SearchNode> {

List<Car> allCars;
List<Customer> allCustomers;
SearchNode parent;

public SearchNode(SearchNode parent, Car currentCar, Customer currentCustomer) {
    // inheriting values from parent search node
    this.allCars = parent.allCars; 
    this.allCustomers = parent.allCustomers;
    this.parent = parent;

    // now updating this node's values
    this.allCustomers.remove(currentCustomer);
}

我检查了一些印刷品,在while(itrCar.hasNext())的第二次迭代中,父母的allCustomer列表缺少currentCustomer

编辑:我宁愿问:有人可以告诉我为什么我更改了父节点的值吗?可能是因为我不了解整个Java实际上是通过引用传递的。

2 个答案:

答案 0 :(得分:4)

您应该创建一个List的副本,您要从该副本中本地删除客户:

public SearchNode(SearchNode parent, Car currentCar, Customer currentCustomer) {
    // inheriting values from parent search node
    this.allCars = parent.allCars; // consider making a copy here too
                                   // if required
    this.allCustomers = new ArrayList<>(parent.allCustomers); // make a 
                                                              // copy
    this.parent = parent;

    // now updating this node's values
    this.allCustomers.remove(currentCustomer); // now you can safely 
                                               // remove currentCustomer
                                               // without affecting 
                                               // parent.allCustomers
}

答案 1 :(得分:0)

所有持有对象(例如列表)的变量本质上都是指针(nitpickers可能不同意确切的术语,但我敢肯定它已经足够接近了)。所以当你说

this.allCustomers = parent.allCustomers

this.allCustomers和parent.allCustomers现在指向同一List对象。然后

this.allCustomers.remove(currentCustomer)

在该列表上运行.remove()...这是父级的allCustomers ...当前正在迭代中。由于列表被修改,因此迭代失败。

为澄清对此问题的评论,“ Java是按值传递100%。对对象的引用是按值传递”。意味着您不能从C语言 [1] 实现函数“交换”,因为该函数获取指针/引用的副本(值)并且不能更改原始值,但是函数可以修改给出其指针/引用的对象的属性,并使那些传入该对象的调用者可以看到那些修改,因为它们引用的是同一对象。基本类型(int,布尔值等)按值传递,但它们是不可变的(可以说x = 3,然后x = 5,但是没有3.changeValueTo(5)),因此,可以忽略大部分差异。

···

[1]:禁止使用Unsafe类,该类可以直接访问内存。不要使用它,因为它可能会严重损坏东西。能够很好地使用它的聪明工程师(例如Java内部人员有时会使用它)无论如何都不需要我的建议。