Stream 作为 Java 8 的集合数据处理利器,是需要着重讲的,以往的复杂操作可能需要写好几行代码,而使用 Stream 后,一行就可以。不过同时也要明白,虽然是一行搞定,但一行按情况的不同最多可能调用好几个方法。
Stream 有着众多的 API,下面一一总结
在讲 API 之前先把创建方式捋一捋吧
- 通过
java.util.Collection.stream()方法用集合创建流
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
- 使用
java.util.Arrays.stream(T[] array)方法用数组创建流
int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
- 使用
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
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
- 依赖与元素顺序的操作,不用并行