Collectors.toMap в Java: повторяющиеся ключи и выбор значения

Collectors.toMap в Java: повторяющиеся ключи и выбор значения
Содержание статьи

Collectors.toMap строит Map из элементов потока. Две функции задают ключ и значение. Если несколько элементов дают одинаковый ключ, нужно определить правило объединения: вариант с двумя аргументами при таком конфликте выбрасывает IllegalStateException.

На Mentoring.digital разберём учебные строки поставки. У одного товара может быть несколько строк, поэтому здесь имеет смысл суммировать количество, а не случайно потерять одну из записей.

Пример с явным объединением

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ToMapDemo {
    record Line(String sku, int quantity) {}

    public static void main(String[] args) {
        var lines = List.of(
            new Line("BOOK", 2),
            new Line("PEN", 3),
            new Line("BOOK", 4)
        );
        Map<String, Integer> quantities = lines.stream().collect(
            Collectors.toMap(
                Line::sku,
                Line::quantity,
                Integer::sum,
                LinkedHashMap::new
            )
        );
        System.out.println(quantities);
    }
}

Пример использует record и рассчитан на Java 17 или новее. Результат: {BOOK=6, PEN=3}. Первая функция извлекает артикул, вторая количество, третья складывает значения при совпадении ключа, четвёртая создаёт Map нужного типа.

Почему нельзя выбрать mergeFunction случайно

Функция (left, right) -> left оставляет ранее накопленное значение, а (left, right) -> right заменяет его новым. Для последовательного упорядоченного потока это позволяет выразить выбор первой или последней записи. Но «последняя в списке» не означает «самая свежая по времени».

Если нужна актуальная версия товара, сравнивайте поле версии или дату по правилам предметной области. При одинаковых датах тоже требуется решение. Если дубль запрещён требованиями, ошибка может быть правильным результатом: не скрывайте её объединением только ради успешного выполнения.

Когда нужен LinkedHashMap

Обычные перегрузки toMap не обещают конкретный тип, изменяемость и потокобезопасность результирующей Map. Если порядок обхода важен для дальнейшего вывода, задайте подходящую фабрику явно. В нашем последовательном примере LinkedHashMap сохраняет порядок первого появления ключей.

Не переносите это наблюдение автоматически на произвольную параллельную обработку. Объединение значений должно быть согласовано с порядком потока и ассоциативно для корректного комбинирования частей. Операция «прибавить количество» и операция «выбрать запись по неоднозначной дате» требуют разных проверок.

Проверки входных данных и выбор коллекции

  • Пустой поток должен давать пустую Map.
  • Один повторяющийся ключ должен обрабатываться по явно выбранному правилу.
  • Пустой артикул и отсутствующее значение нужно проверять до сборки согласно требованиям.
  • Переполнение целого количества следует учитывать, если реальные значения могут быть велики.

Если по каждому ключу нужны все исходные записи, Map одного значения недостаточно. Тогда лучше использовать группировку в списки. В toMap правило объединения оправдано, когда вы действительно хотите получить одно итоговое значение на ключ.

Потоки и коллекции можно освоить на курсах Java. Проверить модель данных и правила обработки дублей поможет наставник по Java.

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