How do I use the compute operations of the map object in Java?

The compute(), computeIfAbsent(), and computeIfPresent() methods introduced in Java 8 provide powerful functionality to modify an existing map in a thread-safe manner.

Here’s an example of how you might use each:

  • compute(): Performs the given mapping function to the entry for the specified key. The function is applied even if key is not present or is null.
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");

map.compute(1, (key, value) -> value + " hundred");

System.out.println(map.get(1)); // prints "one hundred"
  • Word Frequency Count (using compute())

A common use case is when counting the frequency of words in a text. This is where compute() can come in handy:

package org.kodejava.util;

import java.util.HashMap;
import java.util.Map;

public class MapComputeExample {
    public static void main(String[] args) {
        Map<String, Integer> wordCounts = new HashMap<>();
        String sentence = "This is a sample sentence with repeated sample words sample sample";

        for (String word : sentence.split(" ")) {
            wordCounts.compute(word, (key, value) -> value == null ? 1 : value + 1);
        }
        System.out.println("wordCounts = " + wordCounts);
    }
}

In the snippet above, for each word, we increment its count in the wordCounts map, initializing with 1 if the word doesn’t exist yet.

  • computeIfAbsent(): If the specified key is not already associated with a value (or is mapped to null), computes its value using the given mapping function and enters it into this map unless null.
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");

map.computeIfAbsent(4, key -> "four");

System.out.println(map.get(4)); // prints "four"
  • Caching objects (using computeIfAbsent())

Using computeIfAbsent(), you can create a cache that computes values the first time they are requested:

package org.kodejava.util;

import java.util.HashMap;
import java.util.Map;

public class MapComputeIfAbsentExample {
    Map<String, String> cache = new HashMap<>();

    public static void main(String[] args) {
        MapComputeIfAbsentExample demo = new MapComputeIfAbsentExample();
        String imageDir = demo.fetchFromCache("image_dir");
        System.out.println("imageDir = " + imageDir);
    }

    public String fetchFromCache(String key) {
        return cache.computeIfAbsent(key, this::fetchFromDataBase);
    }

    public String fetchFromDataBase(String key) {
        // Simulating actual fetching from a DB
        return "Data for " + key;
    }
}

In this case, whenever data is fetched from the cache, if the key doesn’t exist, computeIfAbsent() will automatically fetch it from the database and store it in the map for future access.

  • computeIfPresent(): If the value for the specified key is present and non-null, attempts to compute a new mapping given the key and its current mapped value.
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");

map.computeIfPresent(1, (key, value) -> value + " hundred");

System.out.println(map.get(1)); // prints "one hundred"
  • Modifying map entries upon certain conditions (using computeIfPresent())

Suppose we have a map of users and their loyalty points. You want to double the points of a user only if the user exists in the map.

package org.kodejava.util;

import java.util.HashMap;
import java.util.Map;

public class MapComputeIfPresentExample {
    public static void main(String[] args) {
        Map<String, Integer> loyaltyPoints = new HashMap<>();
        loyaltyPoints.put("User1", 10);
        loyaltyPoints.put("User2", 20);

        loyaltyPoints.computeIfPresent("User1", (key, value) -> value * 2);

        System.out.println(loyaltyPoints.get("User1")); // prints 20
    }
}

computeIfPresent() will only modify the entries if the keys exactly exist in the map. This can be useful for making conditional updates to a map.

These methods are interesting when you want to modify the map in one atomic operation, which can be useful in multithreaded environments. Moreover, they allow cleaner and more concise code by combining the operations of testing, inserting, removing, and modifying into a single method call.

Wayan

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.