java - 遍历list的时候为什么不能修改呢?
巴扎黑
巴扎黑 2017-04-17 14:57:37
0
4
929

求一个明确的解答,通俗易懂的哈

巴扎黑
巴扎黑

reply all(4)
大家讲道理

The specific language implementation is different, here are some language-independent points

  • If you add data to the array during traversal, it will result in incomplete traversal (because the length of the new member changes), or an infinite loop (because there are always new ones coming in)

  • If data is deleted during traversal, the array access will be out of bounds (because the length is shortened and the pointer points to an area that has been marked as empty)

  • As @Terry_139061’s answer, if you only modify the data of the node itself during traversal, it is generally safe (of course it depends on the specific scenario)

Ty80

The description of your problem is not very accurate. The tags are java and lisp. I have only written a little racket code and will not talk about lisp. The following default modification is the add/remove operation of list

  • First of all, there are many kinds of lists in java:

      java.util.ArrayList;
      java.util.LinkedList;
      java.util.Stack;
      java.util.Vector;
      java.util.concurrent.CopyOnWriteArrayList;

    Among them, CopyOnWriteArrayList will not go wrong when modified during traversal. The implementation method is to separate reading and writing. Please refer to the wiki Copy-on-write

  • Secondly, there are several traversal methods in java:

    for
    iterator
    foreach
    • for, there is no problem in modifying it in the for loop, unless you delete the object you want to access, the array goes out of bounds, or you keep adding to generate an infinite sequence

    • iterator, there will be no problem if you use iterator.remove(), because iterator.remove() will set

      this.expectedModCount = ArrayList.this.modCount;//(1)

      In this way, iterator.next() will not throw an exception after traversing and executing it

      public E next() {
      this.checkForComodification();
      ...
      }
      
      final void checkForComodification() {
      if(ArrayList.this.modCount != this.expectedModCount) {
          throw new ConcurrentModificationException();
      }
      }
    • foreach is essentially an implicit iterator (you can use javap -c to compare the bytecode). Since expectedModCount is not reset, when you use list.remove(), you will traverse and execute iterator.next( ) will tell you ConcurrentModificationException

  • Finally, there is a special thing here. If you delete the second to last value in the list, then when hasNext() is triggered, the result will be false. You will exit the loop and not continue to execute next(), so no error will be reported.

        public boolean hasNext() {
            return this.cursor != ArrayList.this.size;
        }

PS: The JDK used is java-8-openjdk-amd64

Peter_Zhu

It can be modified. The following program will print 3 aaaaa

List<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
list.add("!");
for (int i = 0; i < list.size(); i++) {
    list.set(i, "aaaaa");
}
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

What method did you use to traverse? Please post the code so that I can help you analyze

小葫芦

To answer this question from a java perspective, the Iterator pattern is the standard access method for traversing collection classes. It can abstract access logic from different types of collection classes, thereby avoiding exposing the internal structure of the collection to the client.
For example, if an Iterator is not used, the way to iterate over an array is to use indexing:

  for(int i=0; i<array.size(); i++) { ... get(i) ... } 
  • The client must know the internal structure of the collection in advance. The access code and the collection itself are tightly coupled, and the access logic cannot be separated from the collection class and client code. Each collection corresponds to a traversal method. , client code cannot be reused.

  • What’s even scarier is that if you need to replace ArrayList with LinkedList in the future, the original client code must be completely rewritten. In order to solve the above problems, the Iterator mode always uses the same logic to traverse the collection:

             for(Iterator it = c.iterater(); it.hasNext(); ) { ... }

    - The secret is that the client itself does not maintain the "pointer" for traversing the collection. All internal states (such as the current element position, whether there is a next element) are maintained by Iterator, and this Iterator is generated by the collection class through the factory method. , so it knows how to traverse the entire collection. The client never directly deals with the collection class. It always controls the Iterator and sends it the "forward", "backward", and "get the current element" commands to indirectly traverse the entire collection.

  • When using Iterator to iterate collection elements, collection does not pass the collection element itself to the iteration variable, but passes the value of the collection element to the iteration variable. So modifying the value of the iteration variable does not change the value of the collection element itself. Iterator Iterator does not save objects, it is attached to Collection object and is only used to traverse the collection. Iterator Iterator uses a fail-fast mechanism. Once it is detected that the collection has been modified during the iteration process, the program immediately throws a java.util.ConcurrentModificationException instead of displaying the modified result. This avoids potential problems caused by sharing resources.

public class TestCollections {  
  
    public static void main(String[] args) {  
        List<String> list = new ArrayList<String>();  
        for (int i = 0; i < 10; i++) {  
            list.add("hello_" + i);  
        }  
        Iterator<String> iterator = list.iterator();  
        while (iterator.hasNext()) {  
            String str = iterator.next();  
            System.out.println(str);  
            if (str.equals("hello_5")) {  
//                iterator.remove();  
                list.remove(str);  
            }  
            str = "hehe";  
        }  
        System.out.println(list);  
    }  
}
  • Executing the { str = "hello" } statement will have no impact on external elements. Executing iterator.remove() will delete the current iteration object. Executing list.remove(str) will report java.util.ConcurrentModificationException because it detects that the elements of the collection have been modified.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template