java中Collectors.toMap()的详细的具体用法实例
java中Collectors.toMap()的详细的具体用法实例
Java中的Collectors.toMap()
方法用于将流中的元素收集到一个Map
中。它需要提供两个函数:一个用于生成键(key),一个用于生成值(value)。此外,还可以提供合并函数(用于处理键冲突)和映射工厂(用于指定具体的Map实现)。
方法签名
- 最简单的形式(当键不冲突时):
Collectors.toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
- 带合并函数的(用于解决键冲突):
Collectors.toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction)
- 带合并函数和Map工厂(指定Map的具体实现):
Collectors.toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
实例
示例1:基本用法(无冲突)
假设有一个Person
类:
class Person {
private String id;
private String name;
// 构造方法、getter和setter省略
}
将List<Person>
转换为Map<String, String>
,其中键是id,值是name:
List<Person> people = Arrays.asList(
new Person("1", "Alice"),
new Person("2", "Bob")
);
Map<String, String> idToName = people.stream()
.collect(Collectors.toMap(Person::getId, Person::getName));
// 输出:{1=Alice, 2=Bob}
示例2:键冲突时使用合并函数
如果流中有两个元素的键相同,则会抛出IllegalStateException
。为了避免这种情况,我们可以提供一个合并函数。 例如,有两个人拥有相同的id,我们选择保留后出现的那个人的名字:
List<Person> peopleWithDuplicate = Arrays.asList(
new Person("1", "Alice"),
new Person("1", "Bob")
);
Map<String, String> idToName = peopleWithDuplicate.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(oldValue, newValue) -> newValue)); // 当键冲突时,使用新值覆盖旧值
// 输出:{1=Bob}
示例3:指定Map的具体实现
默认情况下,toMap
会生成一个HashMap
。但我们可以指定其他Map实现,例如TreeMap
。
List<Person> people = Arrays.asList(
new Person("1", "Alice"),
new Person("2", "Bob")
);
TreeMap<String, String> idToNameTree = people.stream()
.collect(Collectors.toMap(
Person::getId,
Person::getName,
(oldValue, newValue) -> newValue, // 合并函数,这里可以选择覆盖
TreeMap::new)); // 指定Map实现为TreeMap
// 输出:{1=Alice, 2=Bob},但类型是TreeMap
示例4:更复杂的场景
假设我们有一个订单列表,每个订单有一个订单号和金额。我们想按订单号分组并计算每个订单的总金额。
class Order {
private String orderId;
private double amount;
// 构造方法、getter和setter省略
}
List<Order> orders = Arrays.asList(
new Order("A", 100.0),
new Order("A", 50.0),
new Order("B", 200.0)
);
Map<String, Double> orderTotal = orders.stream()
.collect(Collectors.toMap(
Order::getOrderId, // 以订单号作为键
Order::getAmount, // 以单个订单金额作为值
Double::sum // 当订单号相同时,金额相加
));
// 输出:{A=150.0, B=200.0}
示例5:值为对象本身
有时我们希望将对象本身作为值放入Map中。
Map<String, Person> idToPerson = people.stream()
.collect(Collectors.toMap(Person::getId, person -> person));
// 或者使用方法引用
Map<String, Person> idToPerson2 = people.stream()
.collect(Collectors.toMap(Person::getId, Function.identity()));
注意事项
- 键冲突:如果没有提供合并函数,且出现重复键,会抛出
IllegalStateException
。 - 空值:如果键映射函数返回null,会抛出
NullPointerException
。值映射函数返回null在Java8中允许,但在Java9及以后,如果值映射函数返回null且没有使用合并函数,也会抛出NullPointerException
。因此,处理可能为null的情况时,应使用合并函数或提前过滤。 - 并行流:合并函数在并行流中用于合并多个部分的结果。
总结
Collectors.toMap()
是一个强大的工具,可以灵活地将流转换为Map。使用时注意处理键冲突和空值问题。 Java中Collectors.toMap()的详细用法与实例 Collectors.toMap()是Java流API中非常强大的收集器,用于将流元素转换为Map集合。下面通过多种场景详细说明其用法:
- 基础用法(键值映射) java 复制 下载 import java.util.; import java.util.stream.;
class Student { private int id; private String name; private double score;
public Student(int id, String name, double score) {
this.id = id;
this.name = name;
this.score = score;
}
public int getId() { return id; }
public String getName() { return name; }
public double getScore() { return score; }
}
public class ToMapExample { public static void main(String[] args) { List students = Arrays.asList( new Student(1, "Alice", 92.5), new Student(2, "Bob", 85.0), new Student(3, "Charlie", 78.5) );
// 创建ID->Name的映射
Map<Integer, String> idToNameMap = students.stream()
.collect(Collectors.toMap(
Student::getId, // 键提取器
Student::getName // 值提取器
));
System.out.println("ID to Name: " + idToNameMap);
// 输出: {1=Alice, 2=Bob, 3=Charlie}
// 创建Name->Score的映射
Map<String, Double> nameToScoreMap = students.stream()
.collect(Collectors.toMap(
Student::getName,
Student::getScore
));
System.out.println("Name to Score: " + nameToScoreMap);
// 输出: {Alice=92.5, Bob=85.0, Charlie=78.5}
}
} 2. 处理键冲突(合并函数) 当遇到重复键时,需要提供合并函数解决冲突:
java 复制 下载 public class ToMapConflictExample { public static void main(String[] args) { List students = Arrays.asList( new Student(1, "Alice", 92.5), new Student(2, "Bob", 85.0), new Student(3, "Charlie", 78.5), new Student(1, "Alex", 88.0) // 重复ID );
// 冲突解决方案1:保留第一个值
Map<Integer, String> keepFirst = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName,
(first, second) -> first // 保留第一个值
));
System.out.println("Keep first: " + keepFirst);
// 输出: {1=Alice, 2=Bob, 3=Charlie}
// 冲突解决方案2:保留最后一个值
Map<Integer, String> keepLast = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName,
(first, second) -> second // 保留最后一个值
));
System.out.println("Keep last: " + keepLast);
// 输出: {1=Alex, 2=Bob, 3=Charlie}
// 冲突解决方案3:合并值
Map<Integer, String> mergeValues = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName,
(first, second) -> first + " & " + second // 合并值
));
System.out.println("Merge values: " + mergeValues);
// 输出: {1=Alice & Alex, 2=Bob, 3=Charlie}
// 复杂合并:计算相同ID学生的平均分
Map<Integer, Double> averageScore = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getScore,
(oldScore, newScore) -> (oldScore + newScore) / 2
));
System.out.println("Average score: " + averageScore);
// 输出: {1=90.25, 2=85.0, 3=78.5}
}
} 3. 指定Map实现类型 java 复制 下载 public class ToMapTypeExample { public static void main(String[] args) { List students = Arrays.asList( new Student(1, "Alice", 92.5), new Student(2, "Bob", 85.0), new Student(3, "Charlie", 78.5) );
// 使用TreeMap按ID排序
Map<Integer, String> treeMap = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName,
(first, second) -> first,
TreeMap::new // 指定Map实现
));
System.out.println("TreeMap: " + treeMap);
// 使用LinkedHashMap保持插入顺序
Map<Integer, String> linkedHashMap = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName,
(first, second) -> first,
LinkedHashMap::new
));
System.out.println("LinkedHashMap: " + linkedHashMap);
// 使用ConcurrentHashMap
Map<Integer, String> concurrentMap = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName,
(first, second) -> first,
ConcurrentHashMap::new
));
System.out.println("ConcurrentHashMap: " + concurrentMap);
}
} 4. 复杂对象作为值 java 复制 下载 public class ToMapObjectExample { public static void main(String[] args) { List students = Arrays.asList( new Student(1, "Alice", 92.5), new Student(2, "Bob", 85.0), new Student(3, "Charlie", 78.5) );
// 值映射到整个对象
Map<Integer, Student> idToStudent = students.stream()
.collect(Collectors.toMap(
Student::getId,
student -> student // 或使用 Function.identity()
));
System.out.println("ID to Student:");
idToStudent.forEach((id, student) ->
System.out.println(id + ": " + student.getName()));
// 创建复杂值对象
Map<String, Map<String, Object>> studentDetails = students.stream()
.collect(Collectors.toMap(
Student::getName,
s -> {
Map<String, Object> details = new HashMap<>();
details.put("id", s.getId());
details.put("score", s.getScore());
details.put("grade", s.getScore() >= 90 ? "A" : "B");
return details;
}
));
System.out.println("\nStudent details:");
studentDetails.forEach((name, details) ->
System.out.println(name + ": " + details));
}
} 5. 处理null值 java 复制 下载 public class ToMapNullExample { public static void main(String[] args) { List students = Arrays.asList( new Student(1, "Alice", 92.5), new Student(2, null, 85.0), // 名字为null new Student(3, "Charlie", null) // 分数为null );
// 1. 过滤null键
Map<Integer, String> filteredKeys = students.stream()
.filter(s -> s.getName() != null)
.collect(Collectors.toMap(
Student::getId,
Student::getName
));
System.out.println("Filtered keys: " + filteredKeys);
// 2. 处理null值
Map<Integer, String> handledValues = students.stream()
.collect(Collectors.toMap(
Student::getId,
s -> s.getName() == null ? "Unknown" : s.getName()
));
System.out.println("Handled values: " + handledValues);
// 3. 使用Optional处理null
Map<Integer, Double> optionalScores = students.stream()
.collect(Collectors.toMap(
Student::getId,
s -> Optional.ofNullable(s.getScore()).orElse(0.0)
));
System.out.println("Scores with default: " + optionalScores);
}
} 6. 高级用法:分组与分区 java 复制 下载 public class ToMapAdvancedExample { public static void main(String[] args) { List students = Arrays.asList( new Student(1, "Alice", 92.5), new Student(2, "Bob", 85.0), new Student(3, "Charlie", 78.5), new Student(4, "David", 92.5), new Student(5, "Eva", 78.5) );
// 按分数分组
Map<Double, List<Student>> groupByScore = students.stream()
.collect(Collectors.groupingBy(Student::getScore));
System.out.println("Grouped by score:");
groupByScore.forEach((score, list) ->
System.out.println(score + ": " +
list.stream().map(Student::getName).collect(Collectors.joining(", "))));
// 分数分区(及格/不及格)
Map<Boolean, List<Student>> passFail = students.stream()
.collect(Collectors.partitioningBy(s -> s.getScore() >= 80));
System.out.println("\nPass/Fail:");
passFail.forEach((passed, list) ->
System.out.println((passed ? "Passed" : "Failed") + ": " +
list.stream().map(Student::getName).collect(Collectors.joining(", "))));
// 创建分数->名字列表的映射
Map<Double, String> scoreToNames = students.stream()
.collect(Collectors.toMap(
Student::getScore,
Student::getName,
(existing, replacement) -> existing + ", " + replacement
));
System.out.println("\nScore to names: " + scoreToNames);
}
} 7. 性能优化技巧 java 复制 下载 public class ToMapPerformanceExample { public static void main(String[] args) { // 创建大量数据 List students = new ArrayList<>(); for (int i = 1; i <= 100000; i++) { students.add(new Student(i, "Student" + i, Math.random() * 100)); }
// 1. 使用并行流
long start = System.currentTimeMillis();
Map<Integer, String> parallelMap = students.parallelStream()
.collect(Collectors.toMap(
Student::getId,
Student::getName
));
System.out.println("Parallel time: " + (System.currentTimeMillis() - start) + "ms");
// 2. 指定初始容量
start = System.currentTimeMillis();
Map<Integer, String> sizedMap = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName,
(f, s) -> s,
() -> new HashMap<>(students.size()) // 指定初始容量
));
System.out.println("Sized map time: " + (System.currentTimeMillis() - start) + "ms");
// 3. 避免不必要的对象创建
start = System.currentTimeMillis();
Map<Integer, String> optimizedMap = new HashMap<>(students.size());
for (Student s : students) {
optimizedMap.put(s.getId(), s.getName());
}
System.out.println("Traditional loop time: " + (System.currentTimeMillis() - start) + "ms");
}
} 常见错误及解决方案 java 复制 下载 public class ToMapCommonErrors { public static void main(String[] args) { List students = Arrays.asList( new Student(1, "Alice", 92.5), new Student(2, "Bob", 85.0), new Student(2, "Bobby", 90.0) // 重复ID );
// 错误1:未处理重复键
try {
Map<Integer, String> errorMap = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName
));
} catch (IllegalStateException e) {
System.out.println("Error: " + e.getMessage());
}
// 解决方案:添加合并函数
Map<Integer, String> solutionMap = students.stream()
.collect(Collectors.toMap(
Student::getId,
Student::getName,
(first, second) -> first + "|" + second
));
System.out.println("Solution: " + solutionMap);
// 错误2:空键
students.add(new Student(3, null, 88.0));
try {
Map<Integer, String> nullKeyMap = students.stream()
.collect(Collectors.toMap(
s -> s.getName().length(), // 可能抛出NPE
Student::getName
));
} catch (NullPointerException e) {
System.out.println("Null key error: " + e.getMessage());
}
// 解决方案:过滤空值
Map<Integer, String> safeMap = students.stream()
.filter(s -> s.getName() != null)
.collect(Collectors.toMap(
s -> s.getName().length(),
Student::getName,
(f, s) -> f
));
System.out.println("Safe map: " + safeMap);
}
} 总结 Collectors.toMap() 的主要用法:
参数 说明 示例 keyMapper 键提取函数 Student::getId valueMapper 值提取函数 Student::getName mergeFunction 键冲突解决函数 (oldVal, newVal) -> oldVal mapSupplier Map实现工厂 TreeMap::new 最佳实践:
始终处理可能的键冲突
考虑空值情况
对于大型数据集,指定初始容量
使用并行流提高性能(当数据量大时)
考虑使用groupingBy()进行更复杂的分组操作
通过合理使用Collectors.toMap(),可以高效地将流数据转换为Map结构,满足各种业务场景的需求。
更多推荐
所有评论(0)