Java函数式数据处理

96 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第26天,点击查看活动详情

1、引入流

流的使用一般包括三件事

  • 一个数据源来执行一个查询
  • 一个中间操作链,形成一条流的流水线
  • 一个终端操作,执行流水线,并能生成结果

流的流水线背后的理念类似于构建器模式。在构建器模式中有一个调用链用来设置一套配置(对流来说这就是一个中间操作链),接着是调用built方法(对流来说就是终端操作)。

下面是中间操作总结

操作返回类型操作参数函数描述符
filterStream<T>Predicat<T>T -> boolean
mapStream<T>Function<T, R>T -R
limitStream<T>
sortedStream<T>Comparator<T>(T, T) -> int
distinctStream<T>

终端操作总结

操作目的
forEach消费流中的每个元素对其应用Lambda。这一操作返回void
count返回流中的元素的个数。这一操作返回long
collect把流归约成一个集合,比如List、Map甚至是Integer

2、使用流

2.1 筛选和切片

筛选各异的元素

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
  .filter(i -> i % 2 == 0)
  .distinct()
  .forEach(System.out::println);

截断流

List<Dish> dishes = menu.stream()
  .filter(d -> d.getCalories() > 300)
  .limit(3)
  .collect(toList());

跳过元素

List<Dish> dishes = menu.stream()
  .filter(d -> d.getCalories() > 300)
  .skip(2)
  .collect(toList());

2.2 映射

流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素。

提取菜名

List<String> dishNames = menu.stream()
  .map(Dish::getName)
  .collect(toList());

返回菜名的长度

List<Integer> dishNameLengths = menu.stream()
  .map(Dish::getName)
  .map(String::length)
  .collect(toList());

2.3 查找和匹配

anyMatch

if (menu.stream().anymatch(Dish::isVegetarian)) {
  System.out.println("The menu is (somewhat) vegetarian friendly!!");
}

allMatch

boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000);

findAny

Optional<Dish> dish = menu.stream()
  .filter(Dish::isVegetarian)
  .findAny();

Optional

Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在。在上面代码中,findAny可能什么元素都没找到。Java8的库设计引入了Optional<T>,这样就不用返回众所周知容易出问题的null了。

  • isPresent() 将在Optional饮食值的时候返回true,否则返回false
  • ifPresent(Consumer<T> block) 会在值存在的进修执行给定的代码块
  • T get() 会在值存在的时候返回值,否则抛出一个NoSuchElement异常
  • T orElse(T other) 会在值存在的时候返回值,否则返回一个默认值
menu.stream()
  .filter(Dish::isVegetarian)
  .findAny()
  .ifPresent(d -> System.out.println(d.getName()))

findFirst

List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree = someNumbers.stream()
  .map(x -> x * x)
  .filter(x -> x % 3 == 0)
  .findFirst();

2.4 归约

求和

int sum = numbers.stream().reduce(0, (a, b) -> a + b);

最大值

Optional<Integer> max = numbers.stream().reduce(Integer::max);

最小值

Optional<Integer> min = numbers.stream().reduce(Integer::min);