Java8 Stream

参考

https://medium.com/javarevisited/you-dont-know-java-streams-in-practice-do-you-826e6aebba81
https://www.baeldung.com/java-8-streams
https://stackify.com/streams-guide-java-8/
《Java8 实战》

创建流

最常用的方法是直接使用 Collection 接口上的 stream 方法从一个容器中创建流:

List<Employee> employees = Arrays.asList(new Employee());
Stream<Employee> stream = employees.stream();
Stream<String> stream = Stream.of("Mary", "Tom", "", "Allen");

如果是数组的话,可以使用 Arrays.stream 方法:

int[] numbers = {2, 3, 5, 7, 11, 13}; 
int sum = Arrays.stream(numbers).

使用 Stream 的 builder 方法创建流:

Stream.Builder<Employee> empStreamBuilder = Stream.builder();

empStreamBuilder.accept(arrayOfEmps[0]);
empStreamBuilder.accept(arrayOfEmps[1]);
empStreamBuilder.accept(arrayOfEmps[2]);

Stream<Employee> empStream = empStreamBuilder.build();

中间操作(intermediate operation)

连接起来的操作称之为中间操作,就像自来水管,一节一节连接起来。中间操作接收一个流并返回另一个流。

filter

过滤符合条件的元素。

// 过滤出 id 为 1 的员工
final List<Employee> collect = employees
    .stream()
    .filter(e -> e.getId() == 1)
    .collect(Collectors.toList());

map

将元素映射成另一个元素:

final List<String> collect = employees.stream()
    .map(Employee::getName)
    .collect(Collectors.toList());

flatMap

拍扁,接受一个将元素转为 Stream 的函数式接口,返回一个将所有元素转换成的流聚合之后的流。

List<String> words = Arrays.asList("aaa", "a", "bc");
final List<String> uniqueCharacters = words.stream()
    .map(w -> w.split(""))
    .flatMap(Arrays::stream)
    .distinct()
    .collect(Collectors.toList());

distinct

对流中的元素去重,使用流中的对象的 equals 方法来判断两个元素是否重复,所以记得按照自己的需要重写 equals 方法和 hashcode 方法。

final List<Employee> collect = employees
    .stream()
    .distinct()
    .collect(Collectors.toList());

limit

和 SQL 中的 limit 关键字一样,limit 会返回一个不超过给定长度的流(限制只保留指定个数的元素)。

// 取流中第一个工资等于 1000 的员工
final List<Employee> collect = employees
        .stream()
        .filter(e -> Objects.nonNull(e) && e.getSalary() == 1_000)
        .limit(1)
        .collect(Collectors.toList());

skip

跳过流中指定个数的元素。

// 先过滤出工资等于 1000 的员工,再跳过前两个员工(从第 3 个员工开始取)
final List<Employee> collect = employees
        .stream()
        .filter(e -> Objects.nonNull(e) && e.getSalary() == 1_000)
        .skip(2)
        .collect(Collectors.toList());

sorted

按照指定的比较器排序流中的元素(如果不传比较器,那么流中的对象需要实现 Comparable 接口)。

// 按照员工的薪资排序
final List<Employee> collect = employees
        .stream()
        .sorted(Comparator.comparing(Employee::getSalary))
        .collect(Collectors.toList());

查找和匹配

allMatch、anyMatch、noneMatch、findFirst、findAny

findAny 不是为了随机选中,而是为了最快的选择出元素,如果 findAny 选择了 Null,会抛出空指针异常

final boolean anyMatch = employees
        .stream()
        .anyMatch(e -> e.getSalary() == 1_000);
final boolean noEmpIsNull = employees.stream().allMatch(Objects::nonNull);
final boolean haveNoOneEmpNameIsTomCat = employees.stream().noneMatch(e -> "TomCat".equals(e.getName()));
final Optional<Employee> firstEmp = employees.stream().findFirst();
final Optional<Employee> anyEmp = employees.parallelStream().findAny();

终端操作(terminate operation)

终端操作就像是水龙头,它的后面不会再有任何的中间操作,终端操作是生成结果的步骤。

collect

将流中的元素收集起来,接收一个 Collector 接口的实现,最常用的就是传入一个将流中的元素采集到一个 List 中的采集器(上面的例子都在使用)。

groupingBy

将流中的元素按照指定的值作为分组依据将流中的元素分组。

// 将员工按照部门进行分组
final Map<String, List<Employee>> groupByEmp = employees
        .stream()
        .collect(Collectors.groupingBy(Employee::getDept));

reducing

将流中的元素合并为一个结果元素。

// 求所有员工的工资总和
final Integer sumSalary = employees
        .stream()
        .collect(Collectors.reducing(0, Employee::getSalary, Integer::sum));

joining

将流中的元素通过指定的分隔符拼接起来。

final String allEmpName = employees.stream().map(Employee::getName).collect(Collectors.joining(", "));

summingInt 和 avging

summingInt 是求和,avging 是求平均值。

final Integer sumSalary = employees.stream().collect(Collectors.summingInt(Employee::getSalary));

maxBy 和 minBy

maxBy 是求最大值,minBy 是求最小值。

Comparator<Employee> empCaloriesComparator = Comparator.comparingInt(Employee::getSalary);

Optional<Employee> salaryMostHighEmp = employees.stream().collect(Collectors.maxBy(empCaloriesComparator));
Optional<Employee> salaryMostLowEmp = employees.stream().collect(Collectors.minBy(empCaloriesComparator));

forEach

对流中的每一个元素执行指定的操作。

// 打印每一个员工
employees.stream().forEach(e -> {
    System.out.println(e.toString());
});

reduce

按照指定的规则将流中的元素合并为一个元素。

// 求所有员工薪水之和
final Integer sumSalary = employees
        .stream()
        .map(Employee::getSalary)
        .reduce(0, Integer::sum);

count

和 SQL 中的 count 一样,统计流中的元素的个数。

// 统计员工数量
final long empNm = employees
        .stream()
        .filter(Objects::nonNull)
        .count();

sum

和 SQL 中的 sum 一样,求流中的元素的和(需要先将流转化为数值类型的流)。

// 求流中所有元素的和
final Integer sumSalary = employees
        .stream()
        .mapToInt(Employee::getSalary)
        .sum();

并行流

并行处理流中的数据。

employees.stream().parallel().forEach(e -> e.salaryIncrement(10.0));
Stream<Product> streamOfCollection = employees.parallelStream();

注意点

  1. 流只能消费一次
List<String> title = Arrays.asList("Java8", "In", "Action"); 
Stream<String> s = title.stream(); 
s.forEach(System.out::println); 
// java.lang.IllegalStateException:流已被操作或关闭
s.forEach(System.out::println);
  1. 流的中间操作是懒加载的

只有执行了终端操作才能触发处理,而且下一步不是需要等到上一步所有的数据都处理完了才开始执行当前步骤,整个处理过程是"流"的。

  • 21
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值