简体   繁体   中英

Can I safely mutate an array I am iterating over, if it returns to its original state after each iteration?

I am writing a minimax algorithm for a game in Java, and, for speed purposes, mutating the game state as I recursively work through the decision tree. However, this involves modifying the list of moves I am iterating over.

public int minimax(int currentDepth) {
    if (currentDepth == depth || board.legalMoves.isEmpty()) {
        int eval = board.eval();
        board.takeBack(1);
        return eval;
    }
    int x = Integer.MIN_VALUE;
    for (Tuple move : board.legalMoves) {
        board.move(move);
        x = max(x, -1*minimax(currentDepth+1));
        board.takeBack(1);
    }
    return x
}

The board.move() method mutates the ArrayList legalMoves , but takeBack(1) brings it back to its original state. Could this cause any problems?

In a word, yes.

You don't specify the type of board.legalMoves . You say that it's array, but it can't be, since you're calling isEmpty() on it. I therefore suspect that you mean ArrayList . If that's the case, the documentation is pretty clear:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException . Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

I see two ways around this:

1) Avoid structural modifications. In other words, it's OK to change the values of the elements, but it's not OK to add/remove elements.

2) Iterate over the ArrayList using indices:

for (int i = 0; i < board.legalMoves.size(); i++) {
    Tuple move = board.get(i);
    ...
}

Yes, you can but it's dangerous. I suggest to move the minmax algorithm to a new class and pass the data to analyze into the constructor.

Now, you can copy the data once in the constructor and the methods can operate on the copy. The algorithm can now modify the data in any way it wishes, it won't influence other parts of the game or other threads. Also, if you have any problems, they must come from the code in one class.

The general goal is to give each modifying part of the code a copy of the data it needs to cut dependencies which can cause trouble.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM