Как характеристика IDENTITY_FINISH меняет работу Collector при завершении сбора?
Характеристика IDENTITY_FINISH сообщает, что функция завершения фактически тождественна: накопленный контейнер можно вернуть как итоговый результат без отдельного вызова finisher. Поэтому реализация Stream API вправе пропустить эту функцию. Объявлять характеристику можно только тогда, когда тип и состояние накопленного контейнера уже соответствуют публичному результату.
Collector отделяет накопление элементов от формирования итогового результата, чтобы поддерживать последовательную и параллельную обработку. Такой контракт позволяет библиотеке объединять частичные контейнеры, а затем при необходимости выполнять финальное преобразование.
Характеристика IDENTITY_FINISH появилась как способ сообщить реализации, что финальное преобразование не добавляет отдельной логики. Это уменьшает лишнюю работу и особенно полезно для сборщиков, результатом которых является сам изменяемый контейнер.
Если finisher выполняет важную операцию — например, делает контейнер неизменяемым, сортирует его или преобразует внутреннюю структуру, — нельзя объявлять IDENTITY_FINISH. Иначе Stream API может вернуть промежуточный контейнер напрямую, минуя обязательную обработку.
Последствиями будут неверный тип результата, утечка изменяемого объекта или нарушение инвариантов, которые должны были устанавливаться в finisher. Ошибка находится в контракте Collector, а не в механизме параллельного выполнения.
У Collector есть типы: T — тип входных элементов, A — тип аккумулятора и R — тип результата. Обычно поток накапливает элементы в объекте типа A, затем функция finisher преобразует A в R.
При наличии IDENTITY_FINISH контракт утверждает, что A можно рассматривать как R без содержательного преобразования. На практике реализация может не вызывать finisher и вернуть аккумулятор напрямую; это оптимизация, разрешённая контрактом, а не гарантия конкретного порядка внутренних вызовов.
Корректный пример выглядит так:
Здесь аккумулятором и результатом является список, поэтому характеристика согласована с контрактом. Однако результат остаётся изменяемым: IDENTITY_FINISH не означает неизменяемость, копирование или защиту от последующих изменений.
Если finisher должен создать неизменяемый список или иной тип результата, характеристику указывать нельзя. В таком случае finisher является обязательной частью логики Collector, хотя при параллельном сборе он обычно применяется после объединения частичных аккумуляторов.
Команда разрабатывает сборщик для получения набора разрешений. В первом варианте аккумулятором служит изменяемый список, а finisher удаляет дубликаты и возвращает неизменяемое представление.
Вариант с IDENTITY_FINISH проще и потенциально дешевле, но некорректен: он позволяет пропустить удаление дубликатов и вернуть изменяемый список. Вариант без этой характеристики сохраняет обязательный finisher, но добавляет финальное преобразование и его стоимость.
Выбран второй вариант, поскольку внешний контракт требует уникальные и неизменяемые разрешения. Оптимизация допустима только после изменения аккумулятора так, чтобы он сам поддерживал нужные инварианты и действительно мог быть безопасно возвращён как итоговый результат.
Нет. Без IDENTITY_FINISH finisher является частью обычного контракта и должен обеспечить преобразование A в R, но детали внутренних оптимизаций не следует трактовать как публичный порядок вызовов. Важен гарантированный итоговый результат, а не наблюдение за внутренним вызовом функции.
Нет. Совпадение Java-типов недостаточно: характеристика описывает семантику, а не только обобщённые параметры. Если finisher сортирует, очищает, копирует, делает объект неизменяемым или иным образом меняет его смысл, это уже не тождественная функция.
Нет, напрямую не связана. Она описывает этап завершения сбора и может применяться как в последовательном, так и в параллельном стриме; параллельность дополнительно требует корректных accumulator и combiner. Характеристика CONCURRENT отвечает за возможность конкурентного накопления, а UNORDERED — за отсутствие требования сохранять порядок, поэтому их нельзя подменять IDENTITY_FINISH.