当前位置:  首页>> 技术小册>> JAVA 函数式编程入门与实践

Stream API入门

在Java 8中,Stream API的引入是函数式编程理念在Java中的一次重大飞跃,它极大地提升了集合(Collection)处理的效率与灵活性。Stream API允许你以声明式方式处理数据集合(包括数组、集合等),通过一系列中间操作(Intermediate Operations)和终端操作(Terminal Operations)的组合,可以简洁地表达复杂的数据处理逻辑。本章将带你走进Stream API的世界,从基础概念到高级应用,逐步掌握这一强大的数据处理工具。

1. Stream API基础概念

1.1 什么是Stream?

Stream API中的“Stream”是一个来自数据源的元素队列并支持聚合操作。和迭代器(Iterator)类似,但Stream API提供了更丰富的操作,如过滤、映射、排序等,且这些操作可以形成流水线式的数据处理。Stream操作分为中间操作和终端操作:

  • 中间操作:返回一个新的Stream,可以链式调用,如filter(), map(), sorted()等。
  • 终端操作:产生一个结果或副作用,如forEach(), collect(), reduce()等,执行后Stream流水线结束。

1.2 Stream的特点

  • 懒执行:Stream的操作是延迟执行的,直到需要结果时才真正开始处理数据,这有助于提高效率。
  • 不可变性:Stream操作不会修改原始数据源,每次操作都会返回一个新的Stream实例。
  • 可消费性:Stream只能被消费一次,一旦执行了终端操作,Stream就不能再被使用了。

2. Stream的创建

Stream API提供了多种方式来创建Stream,包括但不限于以下几种:

  • 集合创建:通过Collection接口的stream()parallelStream()方法创建。
  • 数组创建:使用Arrays.stream(T[] array)静态方法。
  • 静态方法:如Stream.of(T... values)Stream.builder()
  • 文件与IO:Java NIO中的Files类提供了lines(Path path)等方法,用于从文件中读取行作为Stream。
  1. List<String> list = Arrays.asList("apple", "banana", "cherry");
  2. Stream<String> stream = list.stream(); // 从集合创建
  3. Stream<Integer> intStream = Stream.of(1, 2, 3, 4, 5); // 使用静态方法创建

3. 中间操作

中间操作是Stream处理的核心,它们返回一个新的Stream,可以链式调用多个中间操作。

3.1 过滤(Filter)

使用filter(Predicate<? super T> predicate)方法,根据提供的谓词函数过滤Stream中的元素。

  1. List<String> filteredList = list.stream()
  2. .filter(s -> s.startsWith("b"))
  3. .collect(Collectors.toList()); // 收集结果到List

3.2 映射(Map)

map(Function<? super T, ? extends R> mapper)方法将Stream中的每个元素应用给定的函数,并返回一个新的Stream,其中包含应用函数之后的结果。

  1. List<Integer> stringLengthList = list.stream()
  2. .map(String::length)
  3. .collect(Collectors.toList()); // 将字符串列表映射为长度列表

3.3 排序(Sorted)

sorted()sorted(Comparator<? super T> comparator)方法可以对Stream中的元素进行排序。默认是自然排序,也可以提供自定义的Comparator。

  1. List<String> sortedList = list.stream()
  2. .sorted()
  3. .collect(Collectors.toList()); // 自然排序
  4. List<String> customSortedList = list.stream()
  5. .sorted((s1, s2) -> s2.compareTo(s1)) // 逆序排序
  6. .collect(Collectors.toList());

4. 终端操作

终端操作会触发Stream的实际处理,并产生结果或副作用。

4.1 遍历(ForEach)

forEach(Consumer<? super T> action)方法用于遍历Stream中的每个元素,并对每个元素执行提供的操作。

  1. list.stream()
  2. .forEach(System.out::println); // 打印每个元素

4.2 收集(Collect)

collect(Collectors.toCollection(Supplier<C> collectionSupplier))collect(Collectors.toList())等方法可以将Stream的元素收集到List、Set等集合中。

  1. List<String> collectedList = list.stream()
  2. .filter(s -> s.startsWith("b"))
  3. .collect(Collectors.toList()); // 收集过滤后的元素到List

4.3 匹配(Match)

Stream API还提供了anyMatch(), allMatch(), noneMatch()等匹配操作,用于检查Stream中的元素是否满足某些条件。

  1. boolean hasBanana = list.stream()
  2. .anyMatch(s -> s.equals("banana")); // 检查是否包含"banana"

4.4 归约(Reduce)

reduce(BinaryOperator<T> accumulator)方法可以对Stream中的元素进行归约操作,如求和、求积等。

  1. Optional<Integer> sum = list.stream()
  2. .map(String::length) // 映射为长度
  3. .reduce(Integer::sum); // 求和
  4. if (sum.isPresent()) {
  5. System.out.println("Total length: " + sum.get());
  6. }

5. 并行Stream

通过调用集合的parallelStream()方法,可以获取一个并行Stream。并行Stream利用多核处理器,通过多线程处理数据,从而加速处理过程。但需注意,并行Stream并不总是比顺序Stream快,其性能受数据源大小、处理逻辑复杂度、系统资源等多种因素影响。

  1. List<Integer> parallelResult = list.parallelStream()
  2. .map(String::length)
  3. .collect(Collectors.toList()); // 并行处理

6. 实践与注意事项

  • 避免副作用:在Stream操作中,应尽量避免修改数据源或产生其他副作用,以保持Stream操作的纯净性和可预测性。
  • 性能考量:在使用并行Stream时,要考虑到其可能带来的额外开销,以及线程安全等问题。
  • 链式调用:Stream API鼓励链式调用,以简洁地表达复杂的数据处理逻辑。
  • 中间操作与终端操作:理解中间操作与终端操作的区别,有助于更好地利用Stream API。

总结

Stream API是Java 8引入的一项革命性特性,它使得集合处理变得更加灵活和高效。通过中间操作和终端操作的组合,可以简洁地表达复杂的数据处理逻辑。掌握Stream API,不仅能够提高编码效率,还能使代码更加简洁、易读。希望本章内容能够帮助你入门Stream API,并在实际开发中灵活运用这一强大的工具。