Java中foreach使用过多会有性能问题?

最近跟foreach算是很有缘分吧,前一天同事跟我聊一个自己在网上看到的“foreach使用过多会有性能问题,建议使用for i++来做遍历?”我当时一听就纳闷了,以前的时候看到文章说JVM对foreach语法糖是有做优化的,在很多博客也是推荐使用foreach的,为什么突然会有这么个说法呢,今天躺床上看到个博客Java 性能优化的五大技巧的第五点钟第2条中说“避免使用iterator()”,我决定试试究竟!


主机环境

操作系统 Windows10 X64
处理器 i5-2520M CPU @2.50GHZ 双核四线程
内存 6G
Java java version “1.8.0_77”
Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)

执行时间(1000*1000数据量)

  1. ArrayLIst的遍历时间对比(实验结果证明foreach要比for++要差那么些,但是非常接近
long start = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i ++) {
            String s = list.get(i);
        }
        System.out.println("arrayList for i++ on 1000*1000 records time waste: " + (System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        for (String s : list) {
            //
        }
        System.out.println("arrayList foreach on 1000*1000 records time waste: " + (System.currentTimeMillis() - start));

执行结果:
第1次:
arrayList for i++ on 1000*1000 records time waste: 0
arrayList foreach on 1000*1000 records time waste: 16
第2次:
arrayList for i++ on 1000*1000 records time waste: 0
arrayList foreach on 1000*1000 records time waste: 0
第3次:
arrayList for i++ on 1000*1000 records time waste: 0
arrayList foreach on 1000*1000 records time waste: 0

  1. LinkedLIst的遍历时间对比(实验结果证明foreach要比for++好N多数量级倍,N具体多少取决于数据量
for (int i = 0; i < list.size(); i ++) {
            String s = list.get(i);
        }
        System.out.println("LinkedList for i++ on 1000*1000 records time waste: " + (System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        for (String s : list) {
            //
        }
        System.out.println("LinkedList foreach on 1000*1000 records time waste: " + (System.currentTimeMillis() - start));

实验结果
第一次:
LinkedList for i++ on 1000*1000 records time waste: 41224
LinkedList foreach on 1000*1000 records time waste: 11
第二次:
LinkedList for i++ on 1000*1000 records time waste: 43914
LinkedList foreach on 1000*1000 records time waste: 11
第三次:
LinkedList for i++ on 1000*1000 records time waste: 57320
LinkedList foreach on 1000*1000 records time waste: 16

  1. Array的遍历时间对比(实验结果证明foreach要比for++要差那么些,但是非常接近,与ArrayList竟然那么相似
String[] array = (String[])list.toArray(new String[]{});
        long start = System.currentTimeMillis();
        for (int i = 0; i < array.length; i ++) {
            String s = array[i];
        }
        System.out.println("Array for i++ on 1000*1000 records time waste: " + (System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        for (String s : array) {
            //
        }
        System.out.println("Array foreach on 1000*1000 records time waste: " + (System.currentTimeMillis() - start));

实验结果
第一次:
Array for i++ on 1000*1000 records time waste: 0
Array foreach on 1000*1000 records time waste: 0
第二次:
Array for i++ on 1000*1000 records time waste: 0
Array foreach on 1000*1000 records time waste: 16
第三次:
Array for i++ on 1000*1000 records time waste: 0
Array foreach on 1000*1000 records time waste: 0


foreach比for++费内存?

先引用一位哥们的说法:

every time you run into this loop, if strings is an Iterable, you will create a new Iterator instance. If you’re using an ArrayList, this is going to be allocating an object with 3 ints on your heap、浪费空间啊。

关于耗费内存的说法我是支持的,但是”with 3 ints on your heap“这个我就不能同意了,foreach语法糖实际是JVM根据对象生成一个迭代器,我们来看下源码,看下是不是多浪费了空间,浪费了多少空间,现以java.util.ArrayList为例:

public Iterator iterator() {
        return new Itr(); ////这部分是比for++多的
    }

    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;

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

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification(); //这部分是比for++多的
            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];
        }

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

由代码可见,foreach比for++多了new了一个对象,遍历的时候多了是否遍历种被修改判断,其余操作与for++是一致的。由此可见:多浪费空间一说是纯属扯蛋。


总结

  1. foreach遍历线性集合对象比for++略低,但差距非常接近;
  2. foreach遍历链表集合对象比for++高的多,差距由集合对象数量决定
  3. foreach遍历内存使用上会比for++多那么一点点,迭代器实际返回的是集合的数据集;

你可能感兴趣的:(Java基础)