java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Map之computeIfAbsent

Map之computeIfAbsent使用解读

作者:九转成圣

`computeIfAbsent`是Java 8引入的一个Map接口方法,用于简化在Map中获取值的操作,如果指定的键不存在,它会调用提供的函数生成一个新的值,并将其与键关联,这种方法减少了手动检查和插入的样板代码,使代码更加简洁和易读

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)

参数说明

返回值

示例

优化前的代码

在没有使用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;
}

详细解释

实际应用中的示例

示例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方法有以下几个优势:

  1. 简洁性:减少了样板代码,使代码更加简洁和易读。
  2. 避免重复代码:通过使用computeIfAbsent方法,我们避免了显式的空检查和插入逻辑。
  3. 线程安全性:对于某些并发Map实现(如ConcurrentHashMap),computeIfAbsent提供了线程安全的方式来处理映射关系。
  4. 性能提升:在需要缓存计算结果或避免重复计算的场景中,computeIfAbsent可以显著提高性能。
  5. 代码一致性:通过使用computeIfAbsent,代码更加一致和规范,易于维护。

通过理解和使用computeIfAbsent方法,开发者可以写出更简洁、易读且高效的代码,使得在操作map时更加得心应手。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文