## 一、Lambda表达式 #### 1、什么是Lambda表达式 Lambda 表达式是匿名内部类的简化写法,其属于函数式编程思想,它允许将函数当作参数传递给方法,或者将函数作为返回值,这种支持使得 Java 在函数式编程方面更为灵活,能够更好地处理集合操作、并行计算等任务。 以创建线程作为对比 ```java // 使用匿名内部类 new Thread(new Runnable() { @Override public void run() { System.out.println("Hello world !"); } }).start(); // 使用 lambda expression new Thread(() -> System.out.println("Hello world !")).start(); ``` #### 2、语法 ```java (参数列表) -> {方法实现;}; 或 (参数列表) -> 方法实现; 或 单个参数 -> 方法实现; ``` #### 3、变量捕获 Lambda 表达式可以访问外部作用域的变量,这种特性称为变量捕获,Lambda 表达式可以隐式地捕获 final 或事实上是 final 的局部变量。 ```java // 虽然没有final修饰,但不可被修改 int i = 10; new Thread(() -> System.out.println(i + 10)).start(); // 输出20 i = 20; // 抛出异常 ``` #### 4、方法引用 `::` 是方法引用(Method Reference)操作符,它是Lambda表达式的一种简洁写法。当Lambda表达式只是调用一个已存在的方法时,就可以使用方法引用来简化代码。 ```java // 排序 List names = Arrays.asList("John", "Alice", "Bob", "Diana"); // Lambda表达式 names.sort((s1, s2) -> s1.compareTo(s2)); // 方法引用 names.sort(String::compareTo); ``` #### 5、可并行性 Lambda 表达式能够更方便地实现并行操作,通过使用 Stream API 结合 Lambda 表达式,可以更容易地实现并行计算,提高程序性能。 ```java // 使用 Lambda 表达式和 Stream API 进行并行计算 List numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum(); ``` ## 二、流式编程 #### 1、什么是Stream 流(Stream)不是一个数据结构,它不存储数据。相反,它更像是一个来自数据源的高级迭代器,支持一系列操作来流水线式地处理数据。 关键特点: * **不存储数据**:流本身不存储元素,它只是从数据源(如集合、数组)传递数据。 * **不改变源数据**:对流的所有操作(如过滤、映射)都不会修改底层的数据源 * **惰性执行**:中间操作(如 `filter`, `map`)是惰性的,只有在遇到终端操作时才会开始执行。这允许进行短路、循环合并等优化。 * **可消费性**:流像迭代器一样,在生命周期内只能被消费一次。终端操作完成后,流就关闭了。 **注意**:流不是steam #### 2、流式操作的步骤 ##### 创建流 ```java // 从集合创建(最常用) List list = Arrays.asList("a", "b", "c"); Stream streamFromList = list.stream(); // 顺序流 Stream parallelStreamFromList = list.parallelStream(); // 并行流 // 从数组创建 String[] array = {"a", "b", "c"}; Stream streamFromArray = Arrays.stream(array); // 使用 Stream.of 直接传入值 Stream streamOfValues = Stream.of("a", "b", "c"); // 创建数字流(IntStream, LongStream, DoubleStream) IntStream intStream = IntStream.range(1, 5); // 1, 2, 3, 4 (不包含5) IntStream intStreamClosed = IntStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5 ``` ##### 数据操作 对流进行一系列的处理,形成一条处理流水线。数据操作的主要特点为**返回一个新的流**,所以可以链式调用。这些操作是惰性的,不会立即执行。 | 操作 | 描述 | 示例 | | :-------------------------------- | :----------------------------------------------------------- | :------------------------------------------------------------- | | `filter(Predicate)` | **过滤**:保留满足条件的元素 | `stream.filter(s -> s.length() > 3)` | | `map(Function)` | **映射**:将元素转换为另一种形式 | `stream.map(String::toUpperCase)` | | `flatMap(Function>)` | **扁平化映射**:将每个元素转换为一个流,然后把所有流连接起来 | `stream.flatMap(line -> Arrays.stream(line.split(" ")))` | | `distinct()` | **去重** | `stream.distinct()` | | `sorted()` / `sorted(Comparator)` | **排序** | `stream.sorted()` / `stream.sorted(Comparator.reverseOrder())` | | `limit(long n)` | **限制**:只取前 n 个元素 | `stream.limit(5)` | | `skip(long n)` | **跳过**:跳过前 n 个元素 | `stream.skip(2)` | | `peek(Consumer)` | **窥视**:对每个元素执行操作,主要用于调试 | `stream.peek(System.out::println)` | ##### 终端操作 产生一个结果或副作用。终端操作会**触发流的实际执行**,执行后流就被消耗了,不能再使用。 | 操作 | 描述 | 示例 | | :----------------------------------------------- | :----------------------------------------------- | :---------------------------------------- | | `forEach(Consumer)` | **遍历**:对每个元素执行操作 | `stream.forEach(System.out::println)` | | `collect(Collector)` | **收集**:将流转换为集合或其他形式(**最常用**) | `stream.collect(Collectors.toList())` | | `toArray()` | **转换为数组** | `stream.toArray(String[]::new)` | | `toList()` | **转换为列表集合** | `stream.toList(List::of)` | | `reduce(...)` | **归约**:将元素组合起来,产生一个单一值 | `stream.reduce(0, Integer::sum)` | | `min(Comparator)` / `max(Comparator)` | **查找最小/最大值** | `stream.min(String::compareTo)` | | `count()` | **计数** | `stream.count()` | | `anyMatch(Predicate)` / `allMatch` / `noneMatch` | **匹配检查** | `stream.anyMatch(s -> s.startsWith("A"))` | | `findFirst()` / `findAny()` | **查找元素** | `stream.findFirst()` | #### 3、例子 假设我们有一个 `Person` 对象列表,我们想找出所有年龄大于 18 岁的人的名字,并转换为大写,然后放入一个新的列表。 ##### 传统命令式编程(How-to) ```java List people = ...; List result = new ArrayList<>(); for (Person person : people) { if (person.getAge() > 18) { String name = person.getName().toUpperCase(); result.add(name); } } ``` ##### 流式声明式编程(What-to-do) ```java List people = ...; List result = people.stream() // 1. 创建流 .filter(person -> person.getAge() > 18) // 2. 中间操作:过滤年龄 .map(person -> person.getName()) // 2. 中间操作:映射出名字 (可简写为 Person::getName) .map(String::toUpperCase) // 2. 中间操作:映射为大写 .toList(); // 3. 终端操作:收集到List ``` #### 4、收集器 `Collectors` 工具类提供了非常多有用的静态方法,用于将流中的元素汇总成各种形式。 ```java List people = ...; // 转换为 Set (自动去重) Set set = people.stream() .map(Person::getName) .collect(Collectors.toSet()); // 转换为 Map (key: 名字, value: Person对象) // 注意:key不能重复,否则会抛出异常,可用 toMap(k, v, mergeFunction) 解决冲突 Map map = people.stream() .collect(Collectors.toMap(Person::getName, person -> person)); // 分组:按城市分组 Map> peopleByCity = people.stream() .collect(Collectors.groupingBy(Person::getCity)); // 分区:按条件分为 true 和 false 两组 (年龄是否大于18) Map> partitionedPeople = people.stream() .collect(Collectors.partitioningBy(person -> person.getAge() > 18)); // 连接字符串:将所有名字用逗号连接 String joinedNames = people.stream() .map(Person::getName) .collect(Collectors.joining(", ")); // 聚合计算:求平均年龄 Double averageAge = people.stream() .collect(Collectors.averagingInt(Person::getAge)); ``` #### 5、并行流 流式编程的一大优势是易于并行化。只需将 `.stream()` 换成 `.parallelStream()`,Java 就会自动尝试将工作分配到多个线程上执行。 ```java // 顺序流 long count = list.stream().filter(...).count(); // 并行流(对于大数据集,可能更快) long count = list.parallelStream().filter(...).count(); ``` **注意**:并行流不是万能的,它有一定的开销(线程创建、上下文切换、结果合并),只有在数据量足够大、每个元素处理耗时较长时,才能体现出优势。另外,操作必须是**无状态**和**无干扰**的。 Loading... ## 一、Lambda表达式 #### 1、什么是Lambda表达式 Lambda 表达式是匿名内部类的简化写法,其属于函数式编程思想,它允许将函数当作参数传递给方法,或者将函数作为返回值,这种支持使得 Java 在函数式编程方面更为灵活,能够更好地处理集合操作、并行计算等任务。 以创建线程作为对比 ```java // 使用匿名内部类 new Thread(new Runnable() { @Override public void run() { System.out.println("Hello world !"); } }).start(); // 使用 lambda expression new Thread(() -> System.out.println("Hello world !")).start(); ``` #### 2、语法 ```java (参数列表) -> {方法实现;}; 或 (参数列表) -> 方法实现; 或 单个参数 -> 方法实现; ``` #### 3、变量捕获 Lambda 表达式可以访问外部作用域的变量,这种特性称为变量捕获,Lambda 表达式可以隐式地捕获 final 或事实上是 final 的局部变量。 ```java // 虽然没有final修饰,但不可被修改 int i = 10; new Thread(() -> System.out.println(i + 10)).start(); // 输出20 i = 20; // 抛出异常 ``` #### 4、方法引用 `::` 是方法引用(Method Reference)操作符,它是Lambda表达式的一种简洁写法。当Lambda表达式只是调用一个已存在的方法时,就可以使用方法引用来简化代码。 ```java // 排序 List<String> names = Arrays.asList("John", "Alice", "Bob", "Diana"); // Lambda表达式 names.sort((s1, s2) -> s1.compareTo(s2)); // 方法引用 names.sort(String::compareTo); ``` #### 5、可并行性 Lambda 表达式能够更方便地实现并行操作,通过使用 Stream API 结合 Lambda 表达式,可以更容易地实现并行计算,提高程序性能。 ```java // 使用 Lambda 表达式和 Stream API 进行并行计算 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum(); ``` ## 二、流式编程 #### 1、什么是Stream 流(Stream)不是一个数据结构,它不存储数据。相反,它更像是一个来自数据源的高级迭代器,支持一系列操作来流水线式地处理数据。 关键特点: * **不存储数据**:流本身不存储元素,它只是从数据源(如集合、数组)传递数据。 * **不改变源数据**:对流的所有操作(如过滤、映射)都不会修改底层的数据源 * **惰性执行**:中间操作(如 `filter`, `map`)是惰性的,只有在遇到终端操作时才会开始执行。这允许进行短路、循环合并等优化。 * **可消费性**:流像迭代器一样,在生命周期内只能被消费一次。终端操作完成后,流就关闭了。 **注意**:流不是steam #### 2、流式操作的步骤 ##### 创建流 ```java // 从集合创建(最常用) List<String> list = Arrays.asList("a", "b", "c"); Stream<String> streamFromList = list.stream(); // 顺序流 Stream<String> parallelStreamFromList = list.parallelStream(); // 并行流 // 从数组创建 String[] array = {"a", "b", "c"}; Stream<String> streamFromArray = Arrays.stream(array); // 使用 Stream.of 直接传入值 Stream<String> streamOfValues = Stream.of("a", "b", "c"); // 创建数字流(IntStream, LongStream, DoubleStream) IntStream intStream = IntStream.range(1, 5); // 1, 2, 3, 4 (不包含5) IntStream intStreamClosed = IntStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5 ``` ##### 数据操作 对流进行一系列的处理,形成一条处理流水线。数据操作的主要特点为**返回一个新的流**,所以可以链式调用。这些操作是惰性的,不会立即执行。 | 操作 | 描述 | 示例 | | :-------------------------------- | :----------------------------------------------------------- | :------------------------------------------------------------- | | `filter(Predicate<T>)` | **过滤**:保留满足条件的元素 | `stream.filter(s -> s.length() > 3)` | | `map(Function<T, R>)` | **映射**:将元素转换为另一种形式 | `stream.map(String::toUpperCase)` | | `flatMap(Function<T, Stream<R>>)` | **扁平化映射**:将每个元素转换为一个流,然后把所有流连接起来 | `stream.flatMap(line -> Arrays.stream(line.split(" ")))` | | `distinct()` | **去重** | `stream.distinct()` | | `sorted()` / `sorted(Comparator)` | **排序** | `stream.sorted()` / `stream.sorted(Comparator.reverseOrder())` | | `limit(long n)` | **限制**:只取前 n 个元素 | `stream.limit(5)` | | `skip(long n)` | **跳过**:跳过前 n 个元素 | `stream.skip(2)` | | `peek(Consumer<T>)` | **窥视**:对每个元素执行操作,主要用于调试 | `stream.peek(System.out::println)` | ##### 终端操作 产生一个结果或副作用。终端操作会**触发流的实际执行**,执行后流就被消耗了,不能再使用。 | 操作 | 描述 | 示例 | | :----------------------------------------------- | :----------------------------------------------- | :---------------------------------------- | | `forEach(Consumer<T>)` | **遍历**:对每个元素执行操作 | `stream.forEach(System.out::println)` | | `collect(Collector)` | **收集**:将流转换为集合或其他形式(**最常用**) | `stream.collect(Collectors.toList())` | | `toArray()` | **转换为数组** | `stream.toArray(String[]::new)` | | `toList()` | **转换为列表集合** | `stream.toList(List::of)` | | `reduce(...)` | **归约**:将元素组合起来,产生一个单一值 | `stream.reduce(0, Integer::sum)` | | `min(Comparator)` / `max(Comparator)` | **查找最小/最大值** | `stream.min(String::compareTo)` | | `count()` | **计数** | `stream.count()` | | `anyMatch(Predicate)` / `allMatch` / `noneMatch` | **匹配检查** | `stream.anyMatch(s -> s.startsWith("A"))` | | `findFirst()` / `findAny()` | **查找元素** | `stream.findFirst()` | #### 3、例子 假设我们有一个 `Person` 对象列表,我们想找出所有年龄大于 18 岁的人的名字,并转换为大写,然后放入一个新的列表。 ##### 传统命令式编程(How-to) ```java List<Person> people = ...; List<String> result = new ArrayList<>(); for (Person person : people) { if (person.getAge() > 18) { String name = person.getName().toUpperCase(); result.add(name); } } ``` ##### 流式声明式编程(What-to-do) ```java List<Person> people = ...; List<String> result = people.stream() // 1. 创建流 .filter(person -> person.getAge() > 18) // 2. 中间操作:过滤年龄 .map(person -> person.getName()) // 2. 中间操作:映射出名字 (可简写为 Person::getName) .map(String::toUpperCase) // 2. 中间操作:映射为大写 .toList(); // 3. 终端操作:收集到List ``` #### 4、收集器 `Collectors` 工具类提供了非常多有用的静态方法,用于将流中的元素汇总成各种形式。 ```java List<Person> people = ...; // 转换为 Set (自动去重) Set<String> set = people.stream() .map(Person::getName) .collect(Collectors.toSet()); // 转换为 Map (key: 名字, value: Person对象) // 注意:key不能重复,否则会抛出异常,可用 toMap(k, v, mergeFunction) 解决冲突 Map<String, Person> map = people.stream() .collect(Collectors.toMap(Person::getName, person -> person)); // 分组:按城市分组 Map<String, List<Person>> peopleByCity = people.stream() .collect(Collectors.groupingBy(Person::getCity)); // 分区:按条件分为 true 和 false 两组 (年龄是否大于18) Map<Boolean, List<Person>> partitionedPeople = people.stream() .collect(Collectors.partitioningBy(person -> person.getAge() > 18)); // 连接字符串:将所有名字用逗号连接 String joinedNames = people.stream() .map(Person::getName) .collect(Collectors.joining(", ")); // 聚合计算:求平均年龄 Double averageAge = people.stream() .collect(Collectors.averagingInt(Person::getAge)); ``` #### 5、并行流 流式编程的一大优势是易于并行化。只需将 `.stream()` 换成 `.parallelStream()`,Java 就会自动尝试将工作分配到多个线程上执行。 ```java // 顺序流 long count = list.stream().filter(...).count(); // 并行流(对于大数据集,可能更快) long count = list.parallelStream().filter(...).count(); ``` **注意**:并行流不是万能的,它有一定的开销(线程创建、上下文切换、结果合并),只有在数据量足够大、每个元素处理耗时较长时,才能体现出优势。另外,操作必须是**无状态**和**无干扰**的。 最后修改:2025 年 11 月 28 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 👆请简单粗暴地爱我。