jdk8 Stream介绍

java.util.stream.Stream的操作分类,如表所示:

jdk8 Stream介绍

Stream的操作可以分为两大类

  1. 中间操作(Intermediate operations): 对操作进行了记录,但是不立即执行。中间操作又可以分为

1.1 无状态(Stateless)操作: 元素的处理不受之前元素的影响;

1.2 有状态(Stateful)操作: 只有拿到所有元素之后才能继续下去。

  1. 结束操作(Terminal operations):触发实际的计算(即惰性求值)。结束操作又可以分为

2.1 短路操作(short-circuiting): 指遇到某些符合条件的元素就可以得到最终结果;

2.2 非短路操作:必须处理所有元素才能得到最终结果。

1. Stream的操作对象

1.1 数组

Arrays类里面提供了静态方法,用于获取数组的stream

public static<T> Stream<T> stream(T[]array) {

  return stream(array,0,array.length);

}

针对int、long和double这3种基本类型,也提供了对应的IntStream、LongStream、DoubleStream

public static IntStream stream(int[]array) {

  return stream(array,0,array.length);

}

 

public static LongStream stream(long[]array){

  return stream(array,0,array.length);

}

 

public static DoubleStream stream(double[]array){

  return stream(array,0,array.length);

}

也可以分别使用 Stream<Integer>、Stream<Long> >、Stream<Double>,但是 boxing 和 unboxing 会很耗时,所以特别为这三种基本数值型提供了对应的 Stream。

 

也可以通过java.util.stream.Stream的of方法构造

public static<T> Stream<T>of(T t){

  return StreamSupport.stream(newStreams.StreamBuilderImpl<>(t),false);

}

 

1.2 Collection的实现类

jdk1.8以后,Collection接口增加了default方法stream()

default Stream<E> stream() {

  return StreamSupport.stream(spliterator(),false);

}

 

1.3 Map的实现类

虽然Map接口的实现类没有实现Collection接口,但是可以通过Map.entrySet间接使用stream操作:

Map<String, String> map = new HashMap<>(8);

Stream<Entry<String, String>> stream = map.entrySet().stream();

 

2.常见的Stream操作方法

2.1. Intermediate operations 的无状态(Stateless)操作方法

2.1.1 filter方法

Stream <T> filter(Predicate<? super T> predicate);

顾名思义,可以对元素进行过滤操作,过滤规则由Predicate接口的test方法决定:

@FunctionalInterface

public interface Predicate<T>{

  boolean test(T t);

  //…

}

Predicate接口是函数式接口(只有一个未实现方法),可以使用方法引用。举例:

Integer[] sixNums = {1, 2, 3, 4, 5, 6};

Integer[] evens = Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);

 

2.1.2 map方法

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

map方法是对元素进行映射转换,映射规则由Function接口的apply方法决定:

@FunctionalInterface

public interface Function<T,R>{

  R apply(T t);

  //…

}

举例:

List<String> output = wordList.stream().map(String::toUpperCase).collect(Collectors.toList());

 

2.1.3 peek方法

Stream<T> peek(Consumer<? super T> action);

对Stream的每个元素执行额外的操作,Consumer接口声明了void返回值的accept方法:

@FunctionalInterface

public interface Consumer<T>{

    void accept(T t);

    //…

}

 

2.1.4 flapMap方法

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>>mapper);

flatMap方法会先将一个T类型的元素通过mapper映射成一个R元素类型的Stream,最后将这些Stream组合起来返回最终的R类型Stream;举例:

Stream<List<Integer>> inputStream = Stream.of(

 Arrays.asList(1),

 Arrays.asList(2, 3),

 Arrays.asList(4, 5, 6)

 );

Stream<Integer> outputStream = inputStream.

flatMap((childList) -> childList.stream());

flatMap 把input的 Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终output 的新 Stream 里面已经没有 List 了,都是直接的数字。

flatMap方法一般使用在元素类型本来就是数组或集合类的场景。

 

2.1.5 mapToInt&mapToLong&mapToDouble方法

IntStream mapToInt(ToIntFunction<? super T> mapper);

LongStream mapToLong(ToLongFunction<? super T> mapper);

DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

这三个方法是map方法的变种。

 

2.2. Intermediate operations 的有状态(Stateful)操作方法

2.2.1 sorted方法

Stream<T> sorted();

Stream<T> sorted(Comparator<? super T> comparator);

返回一个排序过的Stream。

 

2.2.2 distinct方法

Stream<T> distinct();

返回由不同元素(根据Object的equals(Object)方法)组成的Stream。

 

2.2.3 limit方法

Stream<T> limit(long maxSize);

只保留Stream的前maxSize个元素。

 

2.2.4 skip方法

Stream<T> skip(long n);

与limit方法相反,skip方法抛弃Stream的前n个元素(若元素总数小于n,则返回一个empty Stream)。

 

2.3 Terminal operations的非短路操作方法

2.3.1 forEach方法

void forEach(Consumer<? super T> action);

对于每个元素执行一次最终操作,在并发Stream中,map方法不能保证元素处理顺序;

 

2.3.2 forEachOrdered方法

void forEachOrdered(Consumer<? super T> action);

与forEach方法类似,不同之处在于可以按顺序进行最终操作;

 

2.3.3 toArray方法

Object [] toArray();

<A> A [] toArray(IntFunction<A[]> generator);

将Stream转化为数组,其中第二个toArray方法的函数式接口IntFunction可以得到不同类型数组而不是Object数组,提供了一个apply方法

@FunctionalInterface

public interface IntFunction<R>{

    R apply(int value);

}

一般是传入一个数组构造函数的方法引用,举例:

Person[] men=people.stream()

    .filter(p->p.getGender()==MALE)

    .toArray(Person[]::new);

 

2.3.4 reduce方法

T reduce(T identity, BinaryOperator<T> accumulator);

Optional<T> reduce(BinaryOperator<T> accumulator);

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

BinaryOperator是函数式接口BiFunction的子类,提供了apply方法:

@FunctionalInterface

public interface BiFunction<T, U, R> {

    R apply(T t, U u);

}

对于前2个reduce方法,所有的Stream元素都通过apply方法进行处理,第一个参数t是上一次apply的处理结果,第二个参数u则是将要进行处理的元素。其中第一个reduce方法从初始值identity开始进行,返回值是T类型;第二个reduce方法从第一个Stream元素开始进行,返回值是Optional<T>类型。

举例:

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int sum = Arrays.stream(numbers).reduce(0, (a, b) -> a + b); System.out.println("sum : " + sum); // 55

第3种reduce方法解决了类型转换的问题(前两种reduce方法的元素类型和返回结果类型是相同的)。第一个参数即初始值identity是U类型的;第二个参数BiFunction<U, ? super T, U> accumulator实现了处理和类型转换;第三个参数BinaryOperator<U> combiner是对并行Stream(numList.parallelStream()//得到并行Stream)中不同线程处理得到的部分结果进行结合,返回最终结果(普通条件下不会触发combiner)。

举例:

List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6);
ArrayList<String> result = numList.stream().reduce(new ArrayList<String>(), (a, b) -> {
    a.add("element-" + Integer.toString(b));
    return a;
}, (a, b) -> null);

 

2.3.5 collect方法

<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);

<R, A> R collect(Collector<? super T, A, R> collector);

Supplier接口定义为:

@FunctionalInterface

public interface Supplier<T> {

    T get();

}

BiConsumer接口定义为:

@FunctionalInterface

public interface BiConsumer<T, U> {

    void accept(T t, U u);

}

第1个collect方法相当于做了如下处理:

R result = supplier.get();

for(T element : this stream)

    accumulator.accept(result, element);

return result;

举例:

List<Integer> collectList = Stream.of(1, 2, 3, 4)

    .collect(ArrayList::new, (a,b)->a.add(b),(a,b)->{});

第2个collect方法一般是配合Collectors类进行使用的,举例:

List<Integer> collectList = Stream.of(1, 2, 3, 4)

    .collect(Collectors.toList());

第二种collect方法的使用场景比较多,更为常见。

 

2.3.5 max&min&count方法

不介绍了,请看jdk源码

 

2.4 Terminal operations的短路(short-cutting)操作方法

2.4.1 anyMatch & allMatch&noneMtach 方法

boolean anyMatch(Predicate<? super T> predicate);

boolean allMatch(Predicate<? super T> predicate);

boolean noneMatch(Predicate<? super T> predicate);

只要任意元素满足Predicate就返回true;

所有元素都满足Predicate则返回true;

所有元素都不满足Predicate则返回true。

 

2.4.2 findFirst&findAny方法

Optional<T> findFirst();

Optional<T> findAny();

findFirst:返回Stream中的第一个元素;

findAny:返回的是最快处理完的那个线程的数据(?)。

 

3. 关于Stream的并发处理

BaseStream接口中,定义了获取 并发/顺序 Stream的方法

public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {

 

    S parallel();

 

    boolean isParallel();

 

    S sequential();//默认stream是顺序的

 

    …

}