什么是Stream?

364 阅读3分钟

Stream 作为 Java 8 的集合数据处理利器,是需要着重讲的,以往的复杂操作可能需要写好几行代码,而使用 Stream 后,一行就可以。不过同时也要明白,虽然是一行搞定,但一行按情况的不同最多可能调用好几个方法。

Stream 有着众多的 API,下面一一总结

在讲 API 之前先把创建方式捋一捋吧

  1. 通过 java.util.Collection.stream() 方法用集合创建流
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
  1. 使用java.util.Arrays.stream(T[] array)方法用数组创建流
int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
  1. 使用 Stream 的静态方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println); // 0 2 4 6 8 10

Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);

API

图片.png

of

用于接收一个空的 Stream 对象

empty

创建一个空的 Stream 对象

concat

连接两个 Stream,不改变其中任何一个 Stream 对象,返回一个新的 Stream 对象。

max

用于求数字集合中最大的值,其接收了一个 Comparator,是一个函数式接口类型,用作定义两个对象之间的比较,如下例中 Integer::compareTo 的使用。

private static void max(){
	Stream<Integer> integerStream = Stream.of(1,2,3,4);
	Integer max = integerStream.max(Integer::compareTo).get();
}

// 结合 Lambda 
    
private static void max(){
	Stream<Integer> integerStream = Stream.of(1,2,3,4,5);
	Comparator<Integer> comparator = (x,y) -> (x.intValue() < y.intValue())? -1 : (
	x.equals(y))? 0 : 1);
	Integer max = integerStream.max(comparator).get();
}

min

与 max 用法类似,略

findFirst

获取 Stream 中的第一个元素

findAny

获取 Stream 中的某个元素,串行第一个,并行则不一定。

count

返回元素数量

peek

创建一个通道,在通道中对 Stream 的每个元素进行对应的操作,对应 Consumer 的函数式接口,其可以理解为一个消费接口,用于消费 Stream 元素。

private static void peek(){
	Stream<String> st = Stream.of("a","b","c");
	List<String> list = st.peek(element -> System.out.println(element.toUpperCase())).collect(Collectors.toList());
}

forEach

与 peek 方法类似,都接收一个消费者函数式接口,对每个元素都可以进行操作,与 peek 不同的是,forEach 执行后,此 Stream 就真的消费了,之后的流处理中就没有这个元素了,也不能对其进行后续操作。

forEachOrdered

排了序的forEach

limit

获取前 n 条数据,类似 sql 中的 limit,只能接收一个参数,即数据条数。

private static void limit(){
	Stream<String> st = Stream.of("a","b","c");
	st.limit(2).forEach(element -> ...)
}

skip

与 limit 类似,跳过前几个元素

distinct

去除重复元素

sorted

其有两个重载,一个无参,另一个有 Comparator 类型的参数

无参类型的按照自然顺序进行排序,适合单纯元素,如数字、字母等

有参的需要自定义排序规则

private static void sortedWithComparator(){

	Stream<String> a = Stream.of("a1","a2","a3");
	a.sorted((x,y) -> Integer.parseInt(x.subString(1)) > Integer.parseInt(y.substring(1)) ? 1 : -1)
	.forEach(element -> ...);
}

filter

用于筛选数据

List<Dog> dogs...
Stream<Dog> streams = dogs.stream();
streams.filter(dogs -> dogs.getGender().equals(0) && dogs.getAge() > 50)
.forEach(element -> ...);

map

map 方法的接口方法声明如下,接受一个 Function 函数式接口,通过原属数据元素映射出新的类型。

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

collection

经过以上的各种操作,其实数据并不是我们需要的如 List、Map 这样的数据结构,而是 Stream 类型的数据。collection 就是用来帮我们把最终的结果处理为我们常用的数据结构。

其接口定义为:

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

用一个简单的例子来说明用法

Stream<Integer> integerStream  = Stream.of(1,12,22,33);
List<Integer> list = integerStream.filter(s -> s.intValue() > 7).collect(Collectors.toList());

对于上面的 Collectors.toList() 这个部分,可以等同理解为 (ArrayList::new, ArrayList::add, ArrayList::addAll); Collectors 为我们提供了很多拿来即用的收集器,如 Collectors.toList()Collectors.toSet()Collectors.toMap()Collectors.groupingBy()。其中最后一个是用来分组的,用法如下:

Map<String, List<Dogs>> map = dogs.stream().collect(Collectors.groupingBy(Dog::getAge));

toArray

collection 是用来返回列表或者 map 等数据的,而 toArray 就是用来返回数组,有两个重载,一个空参数,返回的是object[], 另一个则接收IntFunction<R> 类型的参数。

用法如下:

Dog[] dogArray = stream.filter(...).toArray(User[]::new);

reduce

用上次的计算结果进行下个运算处理,并把结果返回,用法如下:

Integer sum = stream.reduce(0, (x, y) -> x+y);

parallelStream

俗称并行 Stream,通过 xxx.parallelStream() 或者 xxx.stream().parallel() 方式来创建并行 Stream 对象,所支持的 API 和普通版本无异,默认使用 ForkJoinPool 线程池,并支持自定义。

注意⚠️:

  • 数据量不大不用并行
  • 最终合并数据也就是 collect 操作时,如果代价很大,不用 Stream
  • 依赖与元素顺序的操作,不用并行