JAVA8 Steam编程 lambda
总结
filter
filter 是 Stream 接口提供的一个中间操作方法。filter 方法的主要作用是根据指定的条件对 Stream 中的元素进行筛选,只保留满足条件的元素,最终返回一个包含筛选后元素的新 Stream。
从功能上,.filter(f(e)) f(e) 是条件判断语句
map
map 是 Stream 接口提供的一个中间操作方法。map 方法用于将流中的每个元素通过一个指定的函数进行转换,然后返回一个包含转换后元素的新流。
从提供的功能可以联想到, map的使用 .map(e -> f(e)) f(e) 是转化函数
flatMap
flatMap 是一个强大的中间操作方法,它主要用于将一个流中的每个元素转换为另一个流,然后将这些流合并成一个单一的流。下面详细介绍 flatMap 的用法。
flatMap 常用于处理嵌套结构的数据,例如一个集合中的每个元素又是一个集合,你想把这些嵌套的集合合并成一个单一的集合。
用法上: flatMap(e -> Arrays.stream(转化为数组) 。 概括的讲, 先转化为列表,再stream
详细用法
filter用法
Stream
T 是 Stream 中元素的类型,predicate 是一个 Predicate 函数式接口,它接收一个类型为 T 的参数,并返回一个布尔值,表示该元素是否满足筛选条件。
Stream<T> filter(Predicate<? super T> predicate);
例子
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FilterEvenNumbersExample {
public static void main(String[] args) {
// 创建一个整数列表
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用 filter 方法筛选出偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 输出结果
System.out.println(evenNumbers);
}
}
map
其中,T 是原始流中元素的类型,R 是转换后元素的类型。mapper 是一个 Function 函数式接口,它接收一个类型为 T 的参数,并返回一个类型为 R 的结果。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamMapExample {
public static void main(String[] args) {
// 创建一个整数列表
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 使用 map 方法对每个元素进行平方操作
List<Integer> squaredNumbers = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// 输出结果
System.out.println(squaredNumbers);
}
}
在上述代码中,map(n -> n * n) 表示对 numbers 流中的每个元素 n 进行平方操作,然后使用 collect(Collectors.toList()) 将转换后的元素收集到一个新的列表中。
2. 将字符串列表中的每个字符串转换为大写
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StringToUpperCaseExample {
public static void main(String[] args) {
// 创建一个字符串列表
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 使用 map 方法将每个字符串转换为大写
List<String> upperCaseWords = words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 输出结果
System.out.println(upperCaseWords);
}
}
这里,map(String::toUpperCase) 使用了方法引用,将 words 流中的每个字符串转换为大写形式。
方法引用的概念
方法引用是一种更简洁的 lambda 表达式语法,它允许直接引用已有的方法或构造函数,而不需要显式地编写 lambda 表达式的参数列表和方法体。方法引用通过 :: 符号来表示,它可以使代码更加简洁易读。
String::toUpperCase 是 e -> e.toUpperCase() 的一种简洁表示方式,它们在功能上是等价的。在实际开发中,可以根据代码的简洁性和可读性需求选择合适的方式。
flatMap
在 Java 8 引入的 Stream API 中,flatMap 是一个强大的中间操作方法,它主要用于将一个流中的每个元素转换为另一个流,然后将这些流合并成一个单一的流。下面详细介绍 flatMap 的用法。
基本概念和语法
Stream 接口中的 flatMap 方法定义如下:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
T是原始流中元素的类型。R是最终流中元素的类型。mapper是一个Function函数式接口,它接收一个类型为T的元素,并返回一个类型为Stream<? extends R>的流。
使用场景
flatMap 常用于处理嵌套结构的数据,例如一个集合中的每个元素又是一个集合,你想把这些嵌套的集合合并成一个单一的集合。
示例代码
1. 处理字符串列表中的单词
假设你有一个字符串列表,每个字符串包含多个用空格分隔的单词,你想把这些单词提取出来形成一个单一的单词列表。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FlatMapStringExample {
public static void main(String[] args) {
List<String> sentences = Arrays.asList("Hello world", "Java programming", "Stream API");
List<String> words = sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
.collect(Collectors.toList());
System.out.println(words);
}
}
代码解释:
sentences.stream():将sentences列表转换为一个流。flatMap(sentence -> Arrays.stream(sentence.split(" "))):对于流中的每个句子,使用split(" ")方法将其拆分为单词数组,然后使用Arrays.stream()将数组转换为流。flatMap会将这些流合并成一个单一的流。collect(Collectors.toList()):将合并后的流中的元素收集到一个列表中。
2. 处理嵌套的集合
假设有一个包含多个列表的列表,你想把这些嵌套的列表合并成一个单一的列表。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FlatMapListExample {
public static void main(String[] args) {
List<List<Integer>> nestedList = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5),
Arrays.asList(6, 7, 8)
);
List<Integer> flattenedList = nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flattenedList);
}
}
代码解释:
nestedList.stream():将nestedList转换为一个流,流中的每个元素是一个List<Integer>。flatMap(List::stream):对于流中的每个列表,使用List::stream方法将其转换为一个流。flatMap会将这些流合并成一个单一的流。collect(Collectors.toList()):将合并后的流中的元素收集到一个列表中。
3. 处理对象中的集合属性
假设你有一个 Person 类,每个 Person 对象有一个 List<String> 类型的 hobbies 属性,你想把所有 Person 对象的 hobbies 合并成一个单一的列表。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class Person {
private String name;
private List<String> hobbies;
public Person(String name, List<String> hobbies) {
this.name = name;
this.hobbies = hobbies;
}
public List<String> getHobbies() {
return hobbies;
}
}
public class FlatMapObjectExample {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("Alice", Arrays.asList("Reading", "Swimming")),
new Person("Bob", Arrays.asList("Running", "Painting")),
new Person("Charlie", Arrays.asList("Gaming", "Hiking"))
);
List<String> allHobbies = persons.stream()
.flatMap(person -> person.getHobbies().stream())
.collect(Collectors.toList());
System.out.println(allHobbies);
}
}
代码解释:
persons.stream():将persons列表转换为一个流。flatMap(person -> person.getHobbies().stream()):对于流中的每个Person对象,获取其hobbies列表并将其转换为一个流。flatMap会将这些流合并成一个单一的流。collect(Collectors.toList()):将合并后的流中的元素收集到一个列表中。
Map computeIfAbsent
示例代码
Map<Long, List<DynamicObject>> orgToBills = new HashMap<>();
for (DynamicObject bill : bills) {
DynamicObject org = (DynamicObject) bill.get(quoteOrgType);
orgToBills.computeIfAbsent((Long)org.getPkValue(), key -> new ArrayList<>()).add(bill);
}
如果当前 key 不存在于 map 中,则使用给定的函数计算出一个 value,并放入 map 中,然后返回这个 value;
如果 key 已经存在,则直接返回已有的 value。
等价JDK7的写法
Long orgPk = (Long) org.getPkValue();
if (!orgToBills.containsKey(orgPk)) {
orgToBills.put(orgPk, new ArrayList<DynamicObject>());
}
orgToBills.get(orgPk).add(bill);
computeIfAbsent其他例子
Map<String, Integer> map = new HashMap<>();
map.put("apple", 1);
Integer result = map.computeIfAbsent("apple", k -> 2); // 返回1,因为"apple"已存在
result = map.computeIfAbsent("banana", k -> 3); // 返回3,因为"banana"不存在,并添加到Map中
Collectors.toMap
Collectors.toMap 是 Java Stream API 中的一个强大工具,用于将流中的元素收集到一个 Map 中。下面详细介绍其用法、参数和常见场景。
基本用法
Collectors.toMap 有三个重载方法,最常用的是接收三个参数的版本:
Map<K, V> map = stream.collect(Collectors.toMap(
keyMapper, // 从元素中提取键的函数
valueMapper, // 从元素中提取值的函数
mergeFunction // 处理键冲突的合并函数
));
参数详解
1. keyMapper
- 作用:从流中的元素中提取键。
- 示例:
List<Person> people = Arrays.asList( new Person(1, "Alice"), new Person(2, "Bob") ); // 将 Person 的 id 作为键,name 作为值 Map<Integer, String> idToName = people.stream() .collect(Collectors.toMap( person -> person.getId(), // 键映射:Person::getId person -> person.getName() // 值映射:Person::getName ));
2. valueMapper
- 作用:从流中的元素中提取值。
- 示例:
// 将 Person 对象本身作为值 Map<Integer, Person> idToPerson = people.stream() .collect(Collectors.toMap( Person::getId, // 键映射 person -> person // 值映射:Function.identity() ));
3. mergeFunction
- 作用:当出现重复键时,指定如何合并值。
- 示例:
List<Person> peopleWithDuplicates = Arrays.asList( new Person(1, "Alice"), new Person(1, "Bob") // 重复的 id=1 ); // 保留最后出现的值(覆盖旧值) Map<Integer, String> idToName = peopleWithDuplicates.stream() .collect(Collectors.toMap( Person::getId, Person::getName, (existing, replacement) -> replacement // 合并函数 ));
常见场景
1. 处理键冲突
- 覆盖旧值:
(oldValue, newValue) -> newValue - 合并值(例如累加):
(oldValue, newValue) -> oldValue + newValue - 抛出异常:
(oldValue, newValue) -> { throw new IllegalStateException(); }
2. 空值处理
- 过滤空值:
Map<Integer, String> nonNullMap = people.stream() .filter(person -> person.getName() != null) // 过滤 null 值 .collect(Collectors.toMap( Person::getId, Person::getName )); - 默认值:
person -> person.getName() != null ? person.getName() : "Unknown"
3. 不可变 Map
import static java.util.stream.Collectors.*;
Map<Integer, String> immutableMap = people.stream()
.collect(Collectors.toUnmodifiableMap(
Person::getId,
Person::getName
));
完整示例
import java.util.*;
import java.util.stream.Collectors;
class Person {
private final int id;
private final String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { return id; }
public String getName() { return name; }
@Override
public String toString() {
return "Person{id=" + id + ", name='" + name + "'}";
}
}
public class Main {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person(1, "Alice"),
new Person(2, "Bob"),
new Person(2, "Charlie") // 重复的 id=2
);
// 使用合并函数处理重复键
Map<Integer, String> idToName = people.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(existing, replacement) -> existing + " & " + replacement // 合并名字
));
System.out.println(idToName); // 输出: {1=Alice, 2=Bob & Charlie}
}
}
注意事项
- 键不可重复:若流中存在重复键且未提供合并函数,会抛出
IllegalStateException。 - 空值风险:键或值为
null可能导致NullPointerException,需提前过滤或处理。 - 顺序问题:若需保持流中元素的顺序,可使用
Collectors.toMap的重载方法并传入LinkedHashMap::new作为第四个参数。
掌握 Collectors.toMap 的用法后,你可以轻松实现集合到映射的转换,处理复杂的数据聚合需求。

浙公网安备 33010602011771号