zoukankan      html  css  js  c++  java
  • 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,也就说明没有下一个了;反之则返回true。

    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的值没有在发生变化,所以也就没有在发生并发修改异常。

  • 相关阅读:
    干货 | 玩转云文件存储——利用CFS实现web应用的共享访问
    干货 | 4步带你完成私有云盘搭建
    是我们控制着技术,还是技术控制着我们?
    如何辨别开发者等级?
    云托管,边缘物理计算&托管物理计算,你所需要了解的……
    干货 | 调用AI api 实现网页文字朗读
    容器技术的未来——京东云技术专访
    隐藏的历史-是什么成就了今天的硅谷?
    ffmpeg windows下编译安装
    比较和打补丁工具
  • 原文地址:https://www.cnblogs.com/donghaibin/p/9195767.html
Copyright © 2011-2022 走看看