Java foreach总结

foreach

foreach循环是Java5针对数组和集合推出的语法糖,Java8开始融入函数式编程和lambda表达式

先看几个简单的例子

package pers.aslania.thinkinjava.submodule;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

/**
 * Created by Aslania on 2018/3/25.
 */
public class ForEach {

    public static void traditionalForeach(Collection collection) {
        Iterator iterator = collection.iterator();
        if (iterator != null) {
            for (; iterator.hasNext();)
                System.out.println(iterator.next());
        }
    }

    public static void traditionalForeach(Object[] arrays) {
        for (int i = 0; i < arrays.length; i++) {
            System.out.println(arrays[i]);
        }
    }

    public static void Java5Foreach(Collection collection) {
        for (Object o : collection) {
            System.out.println(o);
        }
    }

    public static void Java5Foreach(Object[] arrays) {
        for (Object o : arrays) {
            System.out.println(o);
        }
    }

    public static void Java8Foreach(Collection collection) {
        collection.forEach(new Consumer() {
            @Override
            public void accept(Object o) {
                System.out.println(o);
            }
        });
    }

    public static void Java8ForeachWithLambda(Collection collection) {
        collection.forEach(item -> System.out.println(item));
    }

    public static void main(String[] args) {
        Integer[] intArray = new Integer[]{0, 1, 2, 3, 4};
        List intList = Arrays.asList(intArray);

        traditionalForeach(intList);
        traditionalForeach(intArray);

        Java5Foreach(intList);
        Java5Foreach(intArray);

        Java8Foreach(intList);
        Java8ForeachWithLambda(intList);
    }
}

foreach特性总结

  • 仅面向数组、实现了Iterable接口的集合
  • 无法使用索引
  • 无法正常使用return,break,continue等关键字进行流程控制
  • foreach所使用的外部变量只能是final变量
  • foreach不能改变所遍历元素的值
  • 对数组和Java提供的Iterable实现类只能使用正向遍历

foreach深入解析

  • foreach循环的核心在于迭代器,遍历的方式取决于迭代器next()方法的实现,所以对Java提供的Iterable实现类只能使用正向遍历
package pers.aslania.thinkinjava.submodule.foreach;

import java.util.Iterator;

/**
 * @Author: Aslania
 * @Date: Created in 16:37 2018/3/26
 * @Description: Reversed 
 */
public class ReversedList implements Iterable {
    private Node first;
    private Node last;

    public void add(E element) {
        if (last != null) {
            last.next = new Node(last, element, null);
            last = last.next;
        } else {
            first = last = new Node(null, element, null);
        }
    }

    /**
     * foreach循环的关键,利用集合实现的iterator()方法所提供的迭代器来实现集合的循环遍历
     * @return
     */
    @Override
    public Iterator iterator() {
        return new ReversedListIterator(last);
    }

    private class ReversedListIterator implements Iterator {
        private Node current;

        public ReversedListIterator(Node current) {
            this.current = current;
        }

        @Override
        public boolean hasNext() {
            return (null != current) && (null != current.prev);
        }

        /**
         * foreach循环的关键,决定循环遍历的方式,这里采用自定义的反向遍历
         * @return
         */
        @Override
        public E next() {
            if (current != null) {
                E element = current.item;
                current = current.prev;
                return element;
            }
            return null;
        }
    }

    private static class Node {
        E item;
        ReversedList.Node next;
        ReversedList.Node prev;

        Node(ReversedList.Node prev, E element, ReversedList.Node next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

    public static void main(String[] args) {
        ReversedList reversedList = new ReversedList<>();
        for (int i = 0; i < 10; i++) {
            reversedList.add(i);
        }

        reversedList.forEach(item -> System.out.println(item));
    }
}
///////////////////////////////////////////////
/* Output
9
8
7
6
5
4
3
2
1
*/
///////////////////////////////////////////////
  • foreach的实现原理

比较普通的for循环和foreach的代码,可以发现foreach帮开发者简化了很多代码,实际上简化的这部分代码编译器会帮忙补上去。翻阅Iterable接口的foreach方法的默认实现可以发现编译器会将foreach的lambda表达式的内容会以Consume的形式传递给foreach,所以下面Java8Foreach和Java8ForeachWithLambda方法底层代码是相似的

// Iterable.java
default void forEach(Consumersuper T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
    public static void Java8Foreach(Collection collection) {
        collection.forEach(new Consumer() {
            @Override
            public void accept(Object o) {
                System.out.println(o);
            }
        });
    }

    public static void Java8ForeachWithLambda(Collection collection) {
        collection.forEach(item -> System.out.println(item));
    }

了解了这一点后,不难理解下面两点:
1. return/break/continue在foreach的lambda中不能起到预期的作用
2. foreach代码块内不能改变外部变量的值,例如下面代码编译会报错

    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        list.add(i);
    }
    int sum = 0;
    list.forEach(item -> sum += item); // Compiler error: Variable used in lambda expression should be final or effectively final

你可能感兴趣的:(Java)