我们在开发过程中,后端经常和数组、集合打交道,在jdk1.7之前,我们循环一个集合中的内容,要么用for循环,要么用迭代器,代码如下
List<String> strList = Arrays.asList("a", "b", "c", "d");
//方法1
for (int i = 0; i < strList.size(); i++) {
System.out.println(strList.get(i));
}
}
//方法2
for (String str : strList) {
System.out.println(str);
}
// 方法3
Iterator<String> iterator = strList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
而jdk1.8引入新特性Lambda表达式之后则会方便和了很多
strList.forEach(str -> System.out.println(str));
进一步探究,forEach() 是怎么做到的,看下其源码:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
*
* @param <T> the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
forEach() 的形参是一个 Consumer 对象,而 Comsumer 接口又是一个有 @FunctionalInterface 注解的函数式接口,其抽象方法是 accept(T t)。
语法规则
1.当为零个时,箭头左侧的括号不可省略
() -> {System.out.println("test expression!");};
() -> 123;
2.当入参为 1 个时,箭头左侧的圆括号可省略
(x) -> {System.out.println(x);};
x => x + 2;
3.当入参为多个时,左侧括号不能省略
(x, y, z) -> {
System.out.println(x);
System.out.println(y);
System.out.println(z);
};
5.入参类型声明可省略,编译器会做自动类型推断
List<String> strList = Arrays.asList("a", "b", "c", "d");
strList.forEach(str -> {
System.out.println(str);
});
6.表达式右侧的 body 中,只有一条语句,则可省略大括号,否则不可省略
List<String> strList = Arrays.asList("a", "b", "c", "d");
//去掉大括号
strList.forEach(str ->
System.out.println(str)
);
7.在上面使用Lambda表达式之后还可以更简化写法,如下所示
List<String> strList = Arrays.asList("a", "b", "c", "d");
strList.forEach(System.out::println);