forEach是java8的特性之一,它能够大大简化代码的操做,好比有关HashMap的操做:java
HashMap<Integer, String> hashMap = new HashMap<>(3);
hashMap.put(1, "张三");
hashMap.put(2, "李四");
hashMap.put(3, "王五");
for (Map.Entry<Integer, String> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + "," + entry.getValue());
}
复制代码
这是使用映射的视图来遍历整个hashmap来输出键值对的逻辑,输出以下:函数
写起来比较繁琐,看起来也有点累,那么使用forEach就能够简化为以下代码:性能
Map<Integer, String> hashMap = new HashMap<>(3);
hashMap.put(1, "张三");
hashMap.put(2, "李四");
hashMap.put(3, "王五");
hashMap.forEach((k, v) -> System.out.println(k + "," + v));
复制代码
能够发现,它简化了大部分的操做。那么咱们就会有几个问题,好比什么状况下可使用forEach,以及它的底层迭代原理是什么,性能跟传统的foreach相好比何等。测试
点进forEach方法中,能够发现,它是Iterable接口的一个方法,所以能够得出一个结论,只要一个类实现了此接口,那么此类的实例必定可使用forEach方法。ui
同时咱们能够看到,Collection接口继承了此接口。而咱们大部分的集合类接口都继承了Collection接口,具体有Set、List、Map、SortedSet、SortedMap、HashSet、TreeSet、ArrayList、LinkedList、 Vector、Collections、Arrays、AbstractCollection。因此只要是上述的实现类,均可以使用forEach方法。this
让咱们回归Iterable接口,看看接口中的方法:spa
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
复制代码
方法的形参是一个Consumer类型的action,咱们能够猜到,这必定是跟lambda表达式相关的一个东西。事实上,它是一个函数式接口,让咱们看看Consumer:3d
@FunctionalInterface
public interface Consumer<T> {
/** * 可实现方法,接受一个参数且没有返回值 * * @param t the input argument */
void accept(T t);
/** * 返回一个组合的{@code consumer},该组合的{@code consumer}按顺序执行此操做,而后执行 * {@code after}操做。若是执行任一操做时引起异常,则将其中继到组合操做的调用方。若是执行此操做 * 引起异常,则{@code after}操做将不会执行。 * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
复制代码
所以实际上,它仍是使用了foreach来遍历迭代对象,在一个个对参数执行对应的操做。code
为了测试性能,咱们能够编写一个循环,来输出遍历完的时间,具体以下:orm
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
long l = System.currentTimeMillis();
list.forEach(i -> {
});
System.out.println(System.currentTimeMillis() - l);
l = System.currentTimeMillis();
for (Integer s : list) {
}
System.out.println(System.currentTimeMillis() - l);
}
复制代码
输出结果以下:
大约相差了十五倍。那么为何.forEach就会比foreach慢了个十倍左右的数量级呢?细细比较二者区别能够想到forEach多了一个Consumer的声明,那么咱们再来测试一下:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
// 声明Consumer
long l = System.currentTimeMillis();
Consumer<Integer> consumer = integer -> {
};
System.out.println(System.currentTimeMillis() - l);
// forEach
l = System.currentTimeMillis();
list.forEach(consumer);
System.out.println(System.currentTimeMillis() - l);
// foreach
l = System.currentTimeMillis();
for (Integer integer : list) {
}
System.out.println(System.currentTimeMillis() - l);
}
复制代码
输出结果以下:
确实能够得出结论:forEach相比较foreach10倍级的开销大部分都消耗在了实例化Consumer上,迭代器自己并无什么区别。
但forEach效率真的这么低吗?
其实不是的,java会使用预热,当第二次第三次调用forEach的时候,速度不比foreach慢,有可能跟JIT有关。