Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API借助同样新出现的(Lambda表达式、方法引用、接口默认方法等),极大的提高编程效率和程序可读性,同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。
Stream通用语法:
Lambda表达式是函数式编程的一个重大特性,语法结构如下
parameter -> expression body
实际应用代码如下
() -> System.out.println("Hello world!");
a -> System.out.println(a);
(o1, o2) -> o1 - o2
() -> {
System.out.println("这是第一条语句");
System.out.println("这是第二条语句");
}
针对这个 Java Lambda 表达式语法,有几个重要的特征需要说明
An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface’s abstract method count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.
If a type is annotated with this annotation type, compilers are required to generate an error message unless:
The type is an interface type and not an annotation type, enum, or class.
The annotated type satisfies the requirements of a functional interface.
However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.
大致描述下如果想自定义一个函数式接口,必须在接口上加上上FunctionalInterface。
java.util.function 包中定义了大量的函数接口
接口 | 说明 |
---|---|
Consumer | 接收一个参数,无返回值场景:Iterable#forEach |
BiConsumer | 接收两个参数,无返回值场景:Map#forEach |
Predicate | 接收一个参数,返回True/false场景:Stream#filter |
Function | 接收一个参数,返回对象场景:Stream#map |
Comparator | 列表排序:List#sort |
Supplier | 惰性创建对象 |
方法引用是Lambda语法的简写, 提高了代码可读性,方法引用有三种形式
@Test
public void testFunctionReference() {
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5);
list.forEach(this::println);
}
private void println(Object object) {
System.out.println(object);
}
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5);
list.sort(Integer::compare);
private class Person {
String name;
String age;
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
/**
* 第一个参数用于 map ,第二个参数用于 reduce
*/
@Test
public void testMapping() {
List<Person> list = new ArrayList<>();
list.add(new Person("kk", "10"));
list.add(new Person("aa", "10"));
list.add(new Person("cc", "20"));
list.add(new Person("bb", "30"));
Map<String, List<String>> collect = list.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.mapping(Person::getName, Collectors.toList())));
collect.forEach((k, v) -> System.out.println("age=" + k + ",name=" + v));
}
java 8 引入接口默认方法的初衷并不是为了解救一个接口多个实现的痛苦,而是为了向后兼容,以便旧接口也可以使用 Java 8 的 lambda 表达式。
public interface Iterable<T> {
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
Iterator<T> iterator();
// 默认方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
// 默认方法
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5);
Stream<Integer> stream = list.stream();
IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
// 执行结果:345
IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
intStream.filter(i -> i > 2).forEach(System.out::println);
// 执行结果:2345
IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
intStream.skip(1).forEach(System.out::print);
// 执行结果:123
IntStream.of(1, 2, 3, 4, 5).limit(3).forEach(System.out::print);
// 执行结果:12345
IntStream.of(1, 2, 3, 4, 5, 3, 2).distinct().forEach(System.out::print);
// 执行结果:1223345
IntStream.of(1, 2, 3, 4, 5, 3, 2).sorted().forEach(System.out::print);
// 执行结果:2468
IntStream.of(1, 2, 3, 4).map(i -> i * 2).forEach(System.out::print);
// 执行结果:12243648
IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).forEach(System.out::print);
Stream流终端操作是流式处理的最后一步,之前已经对Stream做了一系列的处理之后。该拿出结果了。我们可以在终端操作中实现对流的遍历、查找、归约、收集等等一系列的操作。
@Override
public void forEach(Consumer<? super P_OUT> action) {
evaluate(ForEachOps.makeRef(action, false));
}
注意:由于forEach无法保证排序,所以如果使用parallelStream的时候可能导致数据乱序。
// 执行结果:1
System.out.println(IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).min().getAsInt());
// 执行结果:8
System.out.println(IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).max().getAsInt());
// 执行结果:8
System.out.println(IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).count());
// 执行结果:12243648
int[] ints = IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).toArray();
for (int i : ints) {
System.out.print(i);
}
// 执行结果:true
System.out.println(IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).anyMatch(i -> i == 2));
// 执行结果:false
System.out.println(IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).allMatch(i -> i == 2));
// 执行结果:false
System.out.println(IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).noneMatch(i -> i == 2));
// 执行结果: 1
System.out.println(IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).findFirst().getAsInt());
// 执行结果: 1
System.out.println(IntStream.of(1, 2, 3, 4).flatMap(i -> IntStream.of(i, i * 2)).findAny().getAsInt());
// 执行结果:123
Stream.of("1", "2", "3").collect(Collectors.toList()).forEach(System.out::print);
java.util.stream.Collectors实现各种有用的缩减操作的Collector的实现,例如将元素累积到集合中,根据各种标准汇总元素等。
// 执行结果:{CC=CCCC, BB=BBBB, AA=AAAA}
// 类似Guava的Multimaps#index
System.out.println(Stream.of("AA", "BB", "CC").collect(Collectors.toMap(k -> k, v -> v + v)));
// 执行结果:average:3.533333333333333
List<Double> list = Arrays.asList(1.0D, 2.2D, 3.0D, 4.0D, 5.0D, 6.0D);
Double average = list.stream().collect(Collectors.averagingDouble(d -> d));
System.out.println("average:" + average);
// 先把 [1,2,3,4] 这个集合传递给 s-> s*s lambda 表达式,计算得出结果为 [1,4,9,16] ,然后再把 [1,4,9,16] 传递给 v->v*2 表达式,计算得出 [2,8,18,32] ,然后传递给 Collectors.averagingInt()
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Double average = list.stream().collect(Collectors.collectingAndThen(Collectors.averagingInt(v -> v), v -> 2*v));
System.out.println("average:" + average);
// 执行结果:
// max:6
// min:1
// sum:21
// count:6
// average:3.5
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
IntSummaryStatistics statistics = list.stream().collect(Collectors.summarizingInt(v -> v));
System.out.println("max:" + statistics.getMax());
System.out.println("min:" + statistics.getMin());
System.out.println("sum:" + statistics.getSum());
System.out.println("count:" + statistics.getCount());
System.out.println("average:" + statistics.getAverage());
// 执行结果:1 2 3
Stream.of("1", "2", "3").collect(Collectors.mapping(s -> s + " ", Collectors.toList())).forEach(System.out::print);
// 执行结果:
// age=30,name=[bb]
// age=20,name=[cc]
// age=10,name=[kk, aa]
List<Person> list = new ArrayList<>();
list.add(new Person("kk", "10"));
list.add(new Person("aa", "10"));
list.add(new Person("cc", "20"));
list.add(new Person("bb", "30"));
Map<String, List<String>> collect = list.parallelStream().collect(Collectors.groupingBy(Person::getAge, Collectors.mapping(Person::getName, Collectors.toList())));
collect.forEach((k, v) -> System.out.println("age=" + k + ",name=" + v));