Java

Java 知识量:11 - 45 - 220

8.3 Java流><

Java Stream- 8.3.1 -

Java Stream 是 Java 8 引入的一个新特性,它是函数式编程思想在 Java 中的应用,使得对于集合的操作更加函数化、数学化。

Stream对象是一种高级数据结构,可以由集合、数组、输入/输出流等数据源创建,用于处理集合的元素。

Stream API将集合中的元素进行流式处理,可以链式地调用各种中间操作和终止操作,以实现对数据的复杂处理。

中间操作包括过滤、映射、排序、聚合等操作,用于对Stream中的元素进行转换和处理。例如,使用filter方法过滤出符合条件的元素,使用map方法将每个元素映射成另一个值,使用sorted方法对元素进行排序,使用reduce方法对元素进行聚合操作等等。

终止操作包括收集、连接、遍历、找到第一个元素等操作,用于在Stream处理完成后,对结果进行处理。例如,使用collect方法将Stream中的元素收集到一个集合中,使用forEach方法对每个元素执行特定操作,使用findFirst方法找到第一个符合条件的元素等等。

Stream 的主要特点如下:

  • 惰性求值:Stream 的操作是惰性求值的,这意味着表达式不会立即执行。当操作符需要一个结果,或者需要产生副作用时,才会执行。例如,对于一个过滤和映射操作,只有当终端操作被调用(例如,收集到 List)时,才执行中间操作。

  • 并行和串行流:Stream 可以并行处理,也可以串行处理。默认情况下,Stream 是串行的,这意味着每个操作都会在前一个操作完成之后开始。但是,你可以通过在 Stream 的管道中插入特定的操作符来改变这个行为。例如,stream().parallel() 会创建一个可以并行处理的流。

  • 函数式编程:Stream 的操作都是基于函数式编程的。这意味着它们不会更改原始数据,而是返回一个新的值或结果。这与传统的命令式编程方法形成鲜明对比,后者通常会更改对象或变量的状态。

  • 终端操作:Stream 的操作都是基于终端操作的,比如收集、渲染或返回一个新的集合。这些操作可以创建 Stream 的最终结果。

下面是一个简单的例子,展示了如何使用 Java Stream:

import java.util.Arrays;  
import java.util.List;  
import java.util.stream.Collectors;  
  
public class Main {  
    public static void main(String[] args) {  
        List<String> names = Arrays.asList("John", "Jane", "Jessica", "Jeremy");  
  
        List<String> result = names.stream() // 将集合转化为 Stream  
            .filter(name -> name.startsWith("J")) // 过滤出以 J 开头的名字  
            .map(String::toUpperCase) // 将所有名字转为大写  
            .collect(Collectors.toList()); // 将结果收集到一个新的 List 中  
  
        System.out.println(result); // 输出: [JOHN, JANE, JESSICA, JEREMY]  
    }  
}

这段代码首先创建了一个名字列表,然后通过 Stream API 过滤出所有以 "J" 开头的名字,并将这些名字转换为大写形式,最后将所有的结果收集到一个新的 List 中。

过滤- 8.3.2 -

Java Stream 提供了一种非常直观和灵活的方式来过滤数据。可以使用 filter 方法来应用一个谓词(一个返回布尔值的函数),以筛选出满足条件的元素。

以下是一个示例,演示了如何使用 Java Stream 过滤一个整数列表中的偶数:

import java.util.Arrays;  
import java.util.List;  
import java.util.stream.Collectors;  
import java.util.stream.Stream;  
  
public class FilterExample {  
    public static void main(String[] args) {  
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  
  
        List<Integer> evenNumbers = numbers.stream() // 将列表转换为 Stream  
            .filter(n -> n % 2 == 0) // 应用过滤条件,筛选出偶数  
            .collect(Collectors.toList()); // 将结果收集到一个新的 List 中  
  
        System.out.println(evenNumbers); // 输出: [2, 4, 6, 8, 10]  
    }  
}

在这个例子中,filter 方法接受一个 lambda 表达式 n -> n % 2 == 0,该表达式表示“对于每个元素 n,当 n 是偶数时返回 true”。collect 方法则将过滤后的 Stream 转换回一个 List。

还可以通过方法引用使用已有的方法来作为过滤条件。例如,如果想过滤出能被3整除的数,可以这样做:

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

或者使用方法引用来简化:

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

映射- 8.3.3 -

Stream API提供了非常强大的映射(mapping)功能。映射操作是一种将输入元素转换或操作以产生输出的操作。在Java中,这通常通过map方法来实现。下面是一个简单的例子,它演示了如何使用Stream API进行映射。

假设有一个整数列表,如果希望将每个整数乘以2,可以这样做:

import java.util.Arrays;  
import java.util.List;  
import java.util.stream.Collectors;  
import java.util.stream.Stream;  
  
public class Main {  
    public static void main(String[] args) {  
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  
  
        List<Integer> doubledNumbers = numbers.stream()  
            .map(n -> n * 2) // 将每个元素乘以2  
            .collect(Collectors.toList()); // 将结果收集到列表中  
  
        System.out.println(doubledNumbers); // 输出:[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]  
    }  
}

在上面的代码中,map方法接受一个函数作为参数,这个函数应用于每个元素,并返回一个新的元素。在这个例子中,函数是n -> n * 2,它接受一个整数并返回它的两倍。

另一个常见的映射操作是将一个对象的属性或方法转换为另一个对象。例如,假设有一个Person类,如果想将每个Person对象的name和age属性提取出来,可以这样做:

import java.util.Arrays;  
import java.util.List;  
import java.util.stream.Collectors;  
import java.util.stream.Stream;  
  
class Person {  
    String name;  
    int age;  
  
    Person(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
  
    String getName() {  
        return name;  
    }  
  
    int getAge() {  
        return age;  
    }  
}  
  
public class Main {  
    public static void main(String[] args) {  
        List<Person> people = Arrays.asList(  
            new Person("Alice", 20),  
            new Person("Bob", 25),  
            new Person("Charlie", 30)  
        );  
  
        List<String> names = people.stream()  
            .map(Person::getName) // 将每个Person对象映射为其name属性  
            .collect(Collectors.toList()); // 将结果收集到列表中  
  
        System.out.println(names); // 输出:[Alice, Bob, Charlie]  
    }  
}

在这个例子中,map方法接受一个方法引用作为参数。这里,方法引用Person::getName表示“应用于Person对象的getName方法”。

遍历- 8.3.4 -

Java Stream 提供了一种函数式编程的体验。可以使用 Stream API 来实现遍历,并对数据执行各种操作。

以下是一个简单的例子,展示了如何使用 Java Stream 遍历一个整数列表并对每个元素进行操作:

import java.util.Arrays;  
import java.util.List;  
import java.util.stream.Collectors;  
import java.util.stream.Stream;  
  
public class Main {  
    public static void main(String[] args) {  
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  
  
        List<Integer> squaredNumbers = numbers.stream()  
            .map(n -> n * n) // 对每个元素进行平方操作  
            .collect(Collectors.toList()); // 将结果收集到列表中  
  
        System.out.println(squaredNumbers); // 输出:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]  
    }  
}

在这个例子中,首先创建了一个整数列表 numbers。然后,使用 stream 方法将这个列表转换成一个 Stream。接着,使用 map 方法对 Stream 中的每个元素进行操作(在这个例子中,将每个元素平方)。最后,使用 collect 方法将处理后的 Stream 转换回一个列表。

这就是使用 Java Stream 进行遍历的基本流程。也可以根据需要添加更多的操作,例如 filter(用于筛选元素)、reduce(用于对元素进行累积操作)等等。

归约- 8.3.5 -

Stream的归约操作通常涉及到将流中的元素进行某种累积操作。可以使用reduce方法来实现这种归约操作。reduce方法接受两个参数:一个累加器(用于将流中的每个元素累加到一个累加器中)和一个初始值(用于初始化累加器)。

例如,以下代码使用reduce方法计算一个整数列表的总和:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);  
int sum = numbers.stream()  
                 .reduce(0, Integer::sum);  
System.out.println(sum); // 输出:55

在这个例子中,使用Integer::sum方法作为reduce方法的第二个参数。这是一个方法引用,它调用了Integer类的sum方法。这个方法将它的两个参数相加,然后返回结果。

也可以使用Lambda表达式来代替方法引用:

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

在这个例子中,Lambda表达式接受两个参数(a和b),并将它们相加。

除了求和之外,还可以使用reduce方法执行许多其他类型的归约操作。例如,使用它来计算列表中的最大值或最小值,或者执行更复杂的累积操作。