Map之computeIfAbsent使用解读
作者:九转成圣
Map之computeIfAbsent
在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时更加得心应手。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。