欢迎访问宙启技术站
智能推送

Java函数式编程中的流和集合处理函数

发布时间:2023-05-20 12:43:37

Java 8引进了函数式编程的特性,在此之下,就有一个很重要的概念 - 流。流(Stream)可以看做是一组元素的集合,但是它却具有和传统集合完全不同的操作方式,比如:

- 不支持使用下标索引来获取元素

- 支持操作链(intermediate operation)和终止操作(Terminal operation)

同时,Java 8还引入了一整套全新的流式操作函数,它们可以让我们更加方便、高效地对流进行操作。本文将详细介绍Java 8中的流和集合处理函数。

### 流

在Java 8中,流是一个数据元素序列,在执行超过单个元素的操纵(filter,map等)时,它可以提供更高效的平行执行。

Java 8中,流的特征如下:

- 不存储值的集合。流不会类似于数组或列表那样存储值。它们仅仅是某个源(Collections、Arrays、I/O channel、产生器generator)在中间执行操作过程中输送的值的管道。

- 不改变源对象的情况下进行操作。流操作不会改变源对象。例如,它不会改变列表。而中间操作链中的每个操作都会返回一个新的流。

下面,让我们通过几个例子来看一下Java 8中的流操作。

#### 筛选操作

每一个 Stream 都接受一个指定的输入源。例如, Collection 提供了两个方法 stream()parallelStream() 来获取一个流,并且类型为 Stream<T>,常用的方法有:

- filter():过滤数据

- distinct():去重

- limit():限制数量

- skip():跳过数据

使用流的代码示例:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 5, 6, 7, 8, 9);

List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());

List<Integer> distinctNumbers = numbers.stream().distinct().collect(Collectors.toList());

List<Integer> limitedNumbers = numbers.stream().limit(5).collect(Collectors.toList());

List<Integer> skippedNumbers = numbers.stream().skip(5).collect(Collectors.toList());

#### 映射操作

映射为每一个元素 *x* 建立新元素 *f(x)*,其中 *f* 为一个方法,它把输入值转换成另外一个值。常见的方法有:

- map():映射数据

- flatmap():多个流合并成一个流

使用流的代码示例:

List<String> names = Arrays.asList("apple", "banana", "orange");

List<Integer> nameLengths = names.stream().map(String::length).collect(Collectors.toList());

List<String> words = Arrays.asList("hello", "world");

List<String> charList = words.stream().flatMap(word -> Arrays.stream(word.split(""))).collect(Collectors.toList());

#### 合并操作

常用的合并流的方法有:

- concat():合并两个流为一个流

- distinct():流去重,去掉重复的元素

- peek():观察元素,并在元素上执行后续操作

- forEach():遍历元素

使用流的代码示例:

List<Integer> firstNumbers = Arrays.asList(1, 2, 3);
List<Integer> secondNumbers = Arrays.asList(4, 5, 6);

List<Integer> combinedNumbers = Stream.concat(firstNumbers.stream(), secondNumbers.stream()).collect(Collectors.toList());

List<Integer> distinctNumbers = combinedNumbers.stream().distinct().collect(Collectors.toList());

distinctNumbers.stream().peek(System.out::println).forEach(System.out::println);

### 集合处理函数

Java 8中,提供了一套全新的API,能够更高效、更简洁地处理集合中的元素,它们是:

所有集合函数我们都可以这样分类:

- Predicate(断言)

- Function

- Consumer

- UnaryOperator/ BinaryOperator

#### Predicate(断言)

Predicate接口是Java 8中新增的一个函数式接口,它的作用就是传递一个参数,返回一个布尔值。常用的方法有:

- test():对参数进行判断

使用Predicate实现对集合的筛选,代码示例:

List<String> names = Arrays.asList("Sami", "Gavin", "Frank", "Helen", "Zoey");

Predicate<String> startsWithS = s -> s.startsWith("S");

Predicate<String> longerThan5 = s -> s.length() > 5;

List<String> filteredNames = names.stream().filter(startsWithS.and(longerThan5)).collect(Collectors.toList());

#### Function

Function 接口表示一个函数,它接受一个参数并返回一个结果,常用的方法有:

- apply():对输入参数进行处理后返回结果

使用Function实现对集合元素的转换,代码示例:

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9);

Function<Integer, String> intToString = Object::toString;

List<String> stringNumbers = numbers.stream().map(intToString).collect(Collectors.toList());

#### Consumer

Consumer 接口表示执行特定操作的对象。它接受一个参数,但没有返回值,常用的方法有:

- accept():对输入参数进行处理后不返回结果

使用Consumer实现对集合元素的遍历,代码示例:

List<String> names = Arrays.asList("Sami", "Gavin", "Frank", "Helen", "Zoey");

Consumer<String> printName = name -> System.out.println(name);

names.forEach(printName);

#### UnaryOperator/BinaryOperator

UnaryOperator接口是一种特殊类型的Function,它的输入和输出皆为同一类型。常用的方法有:

- apply():对输入参数进行处理后返回结果

BinaryOperator接口同样是一种特殊类型的Function,它的输入和输出皆为同一类型。但是需要注意,长度为2;并且输入和输出类型相同,组成了一个操作数,常用的方法有:

- apply():对输入参数进行处理后返回结果

使用UnaryOperatorBinaryOperator实现实现集合元素的修改或计算,代码示例:

List<Integer> numbers = Arrays.asList(1,2,3,4,5);

UnaryOperator<Integer> square = n -> n * n;

numbers.replaceAll(square);

BinaryOperator<Integer> add = (n1, n2) -> n1 + n2;

int sum = numbers.stream().reduce(0, add);

### 总结

Java 8中流和集合处理函数为我们提供了更加高效、简洁的编程方式,我们可以通过流的处理函数对集合元素进行筛选、映射等处理,同时,我们也可以使用集合处理函数对集合元素进行修改和计算等操作。当然,Java 8中还提供了很多其他的函数式编程相关的特性,有兴趣的朋友不妨继续深入学习。