在Java编程中,Map
接口提供了一个便捷的方法computeIfAbsent
,它可以用来从map中获取key对应的value。如果value不存在,就使用提供的Function创建一个新的value,然后存入map中,最后返回这个新创建的value
computeIfAbsent方法是Java 8中引入的一种简化操作Map的方式。该方法通过自动检查键值对是否存在并生成缺失的值,减少了手动检查和插入的样板代码。它不仅使代码更加简洁和易读,还提高了操作的效率和一致性。
computeIfAbsent
方法的简介
computeIfAbsent
方法在Java 8中引入,用于简化在Map中获取值的操作。如果指定的键在Map中不存在,computeIfAbsent
会调用指定的函数来生成一个新的值,并将其与键关联。这样,开发者不需要显式地检查键是否存在,从而减少了样板代码。
方法签名
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
参数说明
key
:要在map中查找的键。mappingFunction
:用于生成默认值的函数。
返回值
- 如果
key
已经存在于map中,则返回对应的value。 - 如果
key
不存在于map中,则使用mappingFunction
生成一个新value,存入map,并返回这个新value。
示例
优化前的代码
在没有使用computeIfAbsent
方法之前,我们通常会这样写:
Map<String, Set<Pet>> statistics = new HashMap<>();
Set<Pet> pets = statistics.get(threadName);
if (pets == null) {
pets = new HashSet<>();
statistics.put(threadName, pets);
}
优化后的代码
使用computeIfAbsent
方法后,代码变得更加简洁和易读:
Map<String, Set<Pet>> statistics = new HashMap<>();
Set<Pet> pets = statistics.computeIfAbsent(threadName, k -> new HashSet<>());
工作原理
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
详细解释
- 检查现有值:首先检查指定的键是否已经存在于map中。
- 生成新值:如果键不存在,使用
mappingFunction
生成一个新的值。 - 存储新值:将新生成的值与键关联并存储在map中。
- 返回值:返回与键关联的值(新生成的或已存在的)。
实际应用中的示例
示例1:统计单词出现的次数
我们有一个文本,想统计每个单词出现的次数。我们可以使用computeIfAbsent
方法来简化统计逻辑:
import java.util.*;
public class WordCounter {
public static void main(String[] args) {
String text = "hello world hello Java hello world";
String[] words = text.split(" ");
Map<String, Integer> wordCount = new HashMap<>();
for (String word : words) {
wordCount.computeIfAbsent(word, k -> 0);
wordCount.put(word, wordCount.get(word) + 1);
}
wordCount.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
在这个例子中,computeIfAbsent
方法确保每个单词在第一次出现时被初始化为0,然后我们简单地增加计数值。
示例2:分组学生名单
假设我们有一组学生的成绩记录,我们想按分数段对学生进行分组。例如,分数在90以上的分为一组,80-89分为一组,依此类推。我们可以使用computeIfAbsent
方法来实现这个功能:
import java.util.*;
public class StudentGrouper {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 85),
new Student("Bob", 92),
new Student("Charlie", 87),
new Student("David", 72),
new Student("Eve", 90)
);
Map<String, List<String>> gradeGroups = new HashMap<>();
for (Student student : students) {
String gradeCategory = getGradeCategory(student.getScore());
gradeGroups.computeIfAbsent(gradeCategory, k -> new ArrayList<>()).add(student.getName());
}
gradeGroups.forEach((k, v) -> System.out.println(k + ": " + v));
}
public static String getGradeCategory(int score) {
if (score >= 90) {
return "90-100";
} else if (score >= 80) {
return "80-89";
} else if (score >= 70) {
return "70-79";
} else {
return "Below 70";
}
}
}
class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
}
在这个示例中,computeIfAbsent
方法确保每个分数段(例如“90-100”)被正确初始化为一个新的ArrayList
,然后我们将学生的名字添加到对应的分组中。
示例3:构建依赖图
假设我们有一组任务,每个任务可能依赖于其他任务。我们希望构建一个依赖图,表示每个任务的依赖关系。我们可以使用computeIfAbsent
方法来简化图的构建:
import java.util.*;
public class DependencyGraph {
public static void main(String[] args) {
Map<String, List<String>> dependencies = new HashMap<>();
addDependency(dependencies, "Task1", "Task2");
addDependency(dependencies, "Task1", "Task3");
addDependency(dependencies, "Task2", "Task4");
addDependency(dependencies, "Task3", "Task4");
addDependency(dependencies, "Task4", "Task5");
dependencies.forEach((k, v) -> System.out.println(k + " depends on " + v));
}
public static void addDependency(Map<String, List<String>> dependencies, String task, String dependency) {
dependencies.computeIfAbsent(task, k -> new ArrayList<>()).add(dependency);
}
}
在这个示例中,computeIfAbsent
方法确保每个任务都有一个对应的依赖列表。如果任务不存在,则初始化一个新的ArrayList
并添加依赖关系。
示例4:缓存计算结果
在某些场景中,计算某些值可能非常耗时,因此我们希望缓存计算结果以提高效率。我们可以使用computeIfAbsent
方法来实现简单的缓存:
import java.util.*;
public class FibonacciCache {
private static Map<Integer, Integer> cache = new HashMap<>();
public static void main(String[] args) {
System.out.println(fibonacci(10)); // 输出:55
System.out.println(fibonacci(20)); // 输出:6765
System.out.println(fibonacci(30)); // 输出:832040
}
public static int fibonacci(int n) {
if (n <= 1) {
return n;
}
return cache.computeIfAbsent(n, k -> fibonacci(k - 1) + fibonacci(k - 2));
}
}
在这个示例中,computeIfAbsent
方法用于缓存斐波那契数列的计算结果。对于每一个n,如果缓存中不存在对应的值,则进行计算并缓存结果。这种方法大大提高了计算效率,避免了重复计算。
优势总结
使用computeIfAbsent
方法有以下几个优势:
- 简洁性:减少了样板代码,使代码更加简洁和易读。
- 避免重复代码:通过使用
computeIfAbsent
方法,我们避免了显式的空检查和插入逻辑。 - 线程安全性:对于某些并发Map实现(如
ConcurrentHashMap
),computeIfAbsent
提供了线程安全的方式来处理映射关系。 - 性能提升:在需要缓存计算结果或避免重复计算的场景中,
computeIfAbsent
可以显著提高性能。 - 代码一致性:通过使用
computeIfAbsent
,代码更加一致和规范,易于维护。
通过理解和使用computeIfAbsent
方法,开发者可以写出更简洁、易读且高效的代码,使得在操作map时更加得心应手。