Java for each loop working

前端 未结 5 784
无人及你
无人及你 2021-01-14 08:53

I was working on certain task, when incidentally did something wrong according to me but the code executed and provided correct result. I was little surpris

相关标签:
5条回答
  • 2021-01-14 09:21

    A for-each loop is possible for Classes that implement Iterable. This also means that you can create Classes yourself which you can use in for-each loops, which can be very comfortable.

    This interface forces you to implement a method iterator() which returns an Iterator. Then the for-each loop does nothing but retrieve that iterator and iterate over it using hasNext() and next(). Just the same as you would do it yourself.

    The problem with removing is that when you use a for-each loop and then remove an element from the List, the constructed Iterator will not know anything about that change and there will be a ConcurrentModificationException.

    But if you call Iterator.remove() directly, the Iterator will know about that change and can handle it.

    A common little trick to avoid Iterators and Exceptions at the same time is to do something like this:

    List<Object> objects = new ArrayList<Object>();
    for (Object object : new ArrayList<Object>(objects)) {
        objects.remove(object);
    }
    

    So you create a temporary copy of that List, iterate over that, but call remove on the original List.

    0 讨论(0)
  • 2021-01-14 09:30

    Now I just want to know what is happening behind the every for each loop quoted above

     1. for (String output : splitted) 
        {
            mylist.add(output);
        }
    

    This adds each output String from splitted array to the mylist list.

    2. for (String output : mylist) 
    {
          System.out.println(output);
          mylist = new ArrayList<String>(); //It worked 
          mylist.add(output);
    }
    

    The for statement is governed by the following production:

    for ( FormalParameter : Expression )
                Statement
    

    where Expression must be an instance of java.lang.Iterable, or an array. So this for:each loop is equivalent to this:

    Iterator<String> iterator = mylist.iterator();
    while (iterator.hasNext()) {
        System.out.println(output);
        mylist = new ArrayList<String>(); //It worked 
        mylist.add(output);
    }
    

    Here mylist.iterator() will return a new instance of Iterator type:

    public Iterator<E> iterator() {
            return new Itr();
    }
    

    So even if you are creating new ArrayList instances and assigning them to mylist on each iteration, the iterator obtained from the original mylist will still have a reference to the original mylist and will keep iterating through the elements of original mylist. The iterator keeps a reference to the list it was created on. The assignment mylist = new ArrayList<String>() has no effect on the data that the iterator works on because it changes the variable mylist and not the list itself.

    3. for (String output : mylist) 
        {
            System.out.println(output);             
            mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
        }
    

    Below statement explains this behavior. It is copied from Arraylist doc:

    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.

    4. for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
    {
        String string = (String) iterator2.next();
        System.out.println(string);
        iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
    }
    

    The above statement also explains the behavior of this for loop: the list can be structurally modified by the iterator's own remove or add methods while iterating through the list.

    0 讨论(0)
  • 2021-01-14 09:37

    That is corrent. You cannot modify value of collection that is beeing iterated over using "foreach" loop, and to do that, you have to use collection's iterator.

    0 讨论(0)
  • 2021-01-14 09:40

    for-each loop of List will be internally converted to for loop with iterator.

      for (String output : mylist) 
          {
              System.out.println(output);
              mylist = new ArrayList<String>(); //It worked 
              mylist.add(output);
          }
    

    gets converted to

       for (Iterator<String> iterator = mylist.iterator(); iterator.hasNext();) {
            String output = (String)iterator.next();
            System.out.println(output);
            mylist = new ArrayList<String>(); //It worked 
            mylist.add(output); 
          }
    

    And since the the snapshot of list is already taken at below

    for (Iterator<String> iterator = mylist.iterator(); iterator.hasNext();) {
    

    The loop is running until the last element of list i.e. "how are you".

    Whereas, below is not working because of FailFast behaviour of List.

    for (String output : mylist) 
        {
            System.out.println(output);             
            mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
        }
    

    It says, if you are modifying the list while iterating, with anything other than iterator's own remove method, List will throw ConcurrentModificationException and thats the reason the below is working.

    for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
    {
        String string = (String) iterator2.next();
        System.out.println(string);
        iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
    }
    
    0 讨论(0)
  • 2021-01-14 09:40

    It is of course not a problem to add something to a completely different list than the one you are currently traversing, as you did with the line mylist = new ArrayList<String>(); Even though the variable still has the same name, it will point to an entirely different list.

    The reason why you cannot add something to a list that is currently being "walked through" is, that the internal implementation of that list might not be able to ensure, that you still get the same order of elements and especially not all remaining elements as you would expect. This can be understand best if you imagine that you are using a sorted list: you put in a new element, but whether or not you see that element is undefined, as it depends on where you are and what you insert. As Java doesn't know if you are ok with that, it takes the safe road and throws an Exception.

    There are however lists that are well capable of being able to be modified during traversal, mostly the concurrent lists in the concurrent package.

    0 讨论(0)
提交回复
热议问题