foreach的真面目

Posted donghaibin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了foreach的真面目相关的知识,希望对你有一定的参考价值。

在遍历数组的时候,我们一般使用一般的for循环、增强的for循环,在遍历集合的时候,我们一般使用一般的for循环、增强的for循环、迭代器,当然除了这些我们还可以使用我们的工具类,Arrays和collections来遍历;但是今天我们的“猪脚”是增强for循环,也就是foreach循环。

我们分别看下foreach在数组和在集合遍历的时候,两者有什么区别:

1.在集合中:

a:先看下源码:

public class Demo {
    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();

        list.add("张三");
        list.add("李四");
        list.add("王五");

        for (String string : list) {
            System.out.println(string);
        }
    }
}

b:再看经过反编译后的.class文件:

public class Demo {

   public static void main(String[] args) {
      ArrayList list = new ArrayList();
      list.add("张三");
      list.add("李四");
      list.add("王五");
      Iterator var3 = list.iterator();

      while(var3.hasNext()) {
         String string = (String)var3.next();
         System.out.println(string);
      }

   }
}

可以看出foreach在遍历集合的时候,使用的是Iterator(迭代器)对元素进行遍历的。

2.在数组中:

a:先看下源码:

public class ArrayDemo {
    public static void main(String[] args) {
        int[] arr = { 2, 3, 4, 1, 5 };
        for (int array : arr) {
            System.out.println(array);
        }
    }
}

b:看下被反编译的字节码文件:

public class ArrayDemo {

   public static void main(String[] args) {
      int[] arr = new int[]{2, 3, 4, 1, 5};
      int[] var5 = arr;
      int var4 = arr.length;

      for(int var3 = 0; var3 < var4; ++var3) {
         int array = var5[var3];
         System.out.println(array);
      }

   }
}

可以看出此时的遍历类似于一般的for循环。

所以在遍历数组的时候,使用for循环和foreach循环在效率上是基本上差不多的;在遍历集合的时候,foreach比for循环的效率较高的原因。

那么在遍历集合的时候,使用foreach(底层用的也是迭代器)对其进行操作的时候,为啥会发生如下异常:

Exception in thread "main" java.util.ConcurrentModificationException
 at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
 at java.util.ArrayList$Itr.next(Unknown Source)
 at com.atguigu.add.Demo.main(Demo.java:21)

那么将其的字节码文件也进行反编译一下:

public class Demo {

   public static void main(String[] args) {
      ArrayList list = new ArrayList();
      list.add("张三");
      list.add("李四");
      list.add("王五");
      Iterator var3 = list.iterator();

      while(var3.hasNext()) {
         String string = (String)var3.next();
         list.remove(string);
      }

   }
}

我们看下源码的流程:

 /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

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

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

进入:checkForComodification();

  final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

删除:

 /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

说明一下过程:

a:当var3.hasNext()的时候,底层返回的是 cursor != size;即是下一个元素的索引 != 元素的实际数量,如果等于的话,那么就返回false,也就说明没有下一个了;反之则返回false。

b:当删除第一个元素的时候,并没有报并发修改异常,而是 modCount++,即是modCount增加了1。

c:由于开始的时候,int expectedModCount = modCount;所以此时  int expectedModCount != modCount;

d:所以在 checkForComodification();报出并发修改异常:throw new ConcurrentModificationException();

那么为啥使用迭代器却不会发生并发修改异常哪,有个同学或许已经发现了这个原因,迭代器使用的是它自己都有的删除元素的方法。

 

 public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

 

此时 modCount的值没有在发生变化,所以也就没有在发生并发修改异常。

 

以上是关于foreach的真面目的主要内容,如果未能解决你的问题,请参考以下文章

C#VS快捷键

C#VS快捷键

C#VS快捷键

实用代码片段

方便调试使用的代码片段

vue2.0 代码功能片段