编程中操作集合数据是非常频繁的,使用Java8 中的Stream对集合处理,结合Lambda函数式编程能极大的简化代码,合理的使用Stream能提高代码可读性,另一方面从Java8面世以来Stream API经过了无数项目的实践考验,其稳定性和性能自不必说,网上有很多相关的性能测试案例可以查阅参考,如果有人对你说:Lambda 可读性不好,维护成本高等一些问题,你大可放心,请一定看下最后的注意点。
1. Stream 创建
Stream的创建方式比较多,接下来介绍几种常用的方式,以下Lists使用的google guava的API,直接上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| Stream<String> stream1 = Stream.of("A", "B");
Stream<String> stream2 = Lists.newArrayList("A", "B").stream(); Stream<String> stream3 = Sets.newHashSet("A", "B").stream();
Stream<String> stream4 = Arrays.stream(new String[]{"A", "B"});
Stream<String> stream5 = Files.lines(Paths.get("/file.text"));
IntStream stream6 = Arrays.stream(new int[] { 1, 2, 3 });
Stream<Object> stream7 = Stream.builder().add("A").build();
|
以上创建方式方式2、方式3比较常用,其中方式3也可以使用parallelStream创建并行流,其他的方式可以通过parallel方法转换为并行流,在数据量较大时提高数据处理效率,如下:
1 2 3 4
| Stream<String> stream1 = Lists.newArrayList("A", "B").parallelStream();
Stream<String> stream2 = Arrays.stream(new String[]{"A", "B"}).parallel();
|
2. Stream 中间操作
Stream.map
将原数据处理后生成新的数据,其中mapToInt、mapToLong、mapToDouble方法可直接转换为IntStream、LongStream、DoubleStream(用的比较少,大家可自行查找)
1 2 3 4 5 6
| List<String> result1 = Lists.newArrayList("A") .stream().map(item -> item + "-N").collect(Collectors.toList());
List<String[]> result2 = Lists.newArrayList("A") .stream().map(item -> new String[]{item}).collect(Collectors.toList());
|
Stream.flatMap
合并多个Stream为一个Stream,经常用在合并多个List数据
1 2 3 4
| List<String> result = Lists.newArrayList( Lists.newArrayList("A"), Lists.newArrayList("B") ).stream().flatMap(Collection::stream).collect(Collectors.toList());
|
Stream.filter
元素过滤,可替代循环中的if判断条件,参数为逻辑表达式
1 2
| List<String> result = Lists.newArrayList("A", "B") .stream().filter("A"::equals).collect(Collectors.toList());
|
Stream.distinct
元素去重复,一般用在简单数据类型,如果是对象可以利用TreeSet去重示例如下
1 2 3 4 5 6 7 8
| List<String> result1 = Lists.newArrayList("A", "A", "B") .stream().distinct().collect(Collectors.toList());
List<Demo> result2 = Lists.newArrayList(new Demo()).stream().collect( Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(comparing(Demo::getName))), ArrayList::new) );
|
1 2 3 4 5
| @Data class Demo { private String name; private String age; }
|
Stream.peek
只进行数据处理,不改变原数据类型,和map的区别就是peek接受一个无返回值的操作,一般用于修改对象内部元素
1 2
| List<Demo> result = Lists.newArrayList(new Demo()) .stream().peek(item -> item.setName("A")).collect(Collectors.toList());
|
Stream.sorted
对数据进行排序,支持正序和倒序,并且支持对象类型数据排序
1 2 3 4 5 6
| List<String> result1 = Lists.newArrayList("A", "B") .stream().sorted().collect(Collectors.toList());
List<Demo> result2 = Lists.newArrayList(new Demo()) .stream().sorted(Comparator.comparing(Demo::getName).reversed()).collect(Collectors.toList());
|
Stream.limit
限制最终输出数据的数量,截取流中的元素,默认不进行截取
1 2
| List<String> result1 = Lists.newArrayList("A", "B") .stream().limit(1).collect(Collectors.toList());
|
Stream.skip
跳过前多少个元素,和limit类似,limit是截取流达到限制数量立刻返回流
1 2
| List<String> result = Lists.newArrayList("A", "B") .stream().skip(1).collect(Collectors.toList());
|
3. Stream 终止操作
collect
收集流数据,常用:Collectors.toList(收集为List)、Collectors.joining(收集拼接为String)
1 2 3 4
| List<String> result1 = Lists.newArrayList("A", "B").stream().collect(Collectors.toList());
String result2 = Lists.newArrayList("A", "B").stream().collect(Collectors.joining());
|
reduce
数据聚合为一个值,数据转化为单值后,计算得出一个最终值,这里已累加为例
1
| BigDecimal result = Lists.newArrayList(BigDecimal.valueOf(1), BigDecimal.valueOf(2)).stream().reduce(BigDecimal.ZERO, BigDecimal::add);
|
allMatch、anyMatch、noneMatch
1 2 3 4 5 6
| boolean result1 = Lists.newArrayList(1, 2, 3, 4).stream().allMatch(item -> item > 1);
boolean result2 = Lists.newArrayList(1, 2, 3, 4).stream().anyMatch(item -> item > 1);
boolean result3 = Lists.newArrayList(1, 2, 3, 4).stream().noneMatch(item -> item > 1);
|
count
统计数据数量值
1
| long result1 = Lists.newArrayList(1, 2, 3, 4).stream().count();
|
findAny、findFirst
如果存在数据,都返回一条,区别是在并行处理中,findAny匹配到数据就返回,findFirst需要等所有数据处理完成返回第一条,所以在并行处理中findAny效率更高
1 2 3 4
| Integer result1 = Lists.newArrayList(1, 2, 3, 4).stream().findAny().get();
Integer result12= Lists.newArrayList(1, 2, 3, 4).parallelStream().findFirst().get();
|
forEach、forEachOrdered
遍历所有元素,比如输出操作,有了forEach为什么还需要forEachOrdered呢,主要是在并行执行中,元素执行是没有顺序的,forEachOrdered能将结果按照顺序输出
1 2 3 4
| Lists.newArrayList(1, 2, 3, 4).stream().forEach(System.out::println);
Lists.newArrayList(1, 2, 3, 4).parallelStream().forEachOrdered(System.out::println);
|
max、min
获取流中元素最大和最小的值,以下举例最大值得获取,最小值同理
1 2 3 4
| Integer result = Lists.newArrayList(1, 2, 3, 4).stream().max(Integer::compare).get();
Demo result = Lists.newArrayList(new Demo()).stream().max(comparing(Demo::getAge)).get();
|
4. Stream 注意点
在使用并行流进行处理时,一定需要收集最终数据,否则可能会丢失数据,比如使用collect或者reduce收集数据,也就是说使用了collect和reduce才能使用parallelStream,此时整个流处理是线程安全的。