ComputeIfAbsent в Java: создать значение Map только при необходимости

ComputeIfAbsent в Java: создать значение Map только при необходимости
Содержание статьи

Чтобы собрать элементы по ключу, часто нужен список внутри Map. computeIfAbsent получает существующее непустое значение или вычисляет новое, если ключ отсутствует либо связан с null. В примере Mentoring.digital будем добавлять имена в группу без отдельного блока проверки.

Создайте список для первого элемента группы

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Map<String, List<String>> groups = new HashMap<>();
groups.computeIfAbsent("backend", key -> new ArrayList<>())
      .add("Ada");
groups.computeIfAbsent("backend", key -> new ArrayList<>())
      .add("Lin");

System.out.println(groups.get("backend")); // [Ada, Lin]

В первый раз функция создаёт ArrayList и помещает его в карту. Во второй раз в карте уже есть список, поэтому возвращается он же. Метод add вызывается на полученном списке, а не на Map.

Фрагмент предназначен для тела метода или интерактивного окружения Java. В обычном файле приложения добавьте класс и main, а импорты оставьте перед объявлением класса. Здесь рассматривается последовательное выполнение в одном потоке.

Разберите поведение null

Если карта разрешает null и ключ связан с ним, computeIfAbsent тоже попытается вычислить значение. Когда сама функция вернула null, новое значение в карту не добавляется. Поэтому последующий вызов может снова запустить вычисление.

Map<String, String> names = new HashMap<>();
String value = names.computeIfAbsent("missing", key -> null);
System.out.println(value); // null
System.out.println(names.containsKey("missing")); // false

Не вызывайте метод результата без проверки, если функция действительно может вернуть null. В примере со списком конструктор возвращает список, поэтому этой неопределённости нет. Но при загрузке данных из внешнего источника условие может отличаться.

Отличайте вычисление от готового значения

В putIfAbsent передаётся уже вычисленное значение. Выражение, которое его создаёт, будет вычислено до самого вызова метода. В computeIfAbsent передаётся функция: она нужна только при отсутствии непустого значения.

Не меняйте эту же карту внутри функции вычисления. И не приписывайте обычному HashMap потокобезопасность из-за названия метода. Гарантии атомарности зависят от конкретной реализации Map; совместное изменение вложенного ArrayList тоже требует отдельного решения.

Практическое задание: сгруппируйте три заявки по status. Проверьте повторный статус, новый статус и пустой входной список. Затем объясните, почему замена HashMap на ConcurrentHashMap сама по себе не делает ArrayList безопасным для одновременных add.

Коллекции и работу с функциями можно изучать на курсе Java с наставником. Для разбора конкретной модели данных подойдёт ментор по Java.

Похожие статьи по данной теме