понедельник, 25 января 2016 г.

Collect своими руками

После выхода java 8 появилась возможность писать в pipeline стиле. Т.е. создавать последовательность операций которые будут обрабатывать элементы из некоторого источника, будь то коллекция, бесконечная и конечная генерирующая функция, строки из файла и пр. Это тема достаточно обширная и она не будет освещена в данном топике. Расскажу про collector'ы. Это такие объекты с помощью которых, можно привести результат обработки элементов к определенному виду. Существует несколько встроенных, в классе java.util.stream.Collectors. Например, там есть toList, он создает список из элементов потока. Есть и более сложные, например, groupingBy, группирующий элементы по определенному критерию. Мы же напишем свой, который будет группировать строки по начальным буквам.
И так, для начала, ознакомимся с интерфейсом java.util.stream.Collector от которого будет наследоваться:

В нем пять функций. Некоторые из них не обязательно реализовывать честно, достаточно оставить пустое тело, но об этом позже.
  1. supplier создает вспомогательный объект производящий основные манипуляции над элементами.
  2. accumulator функция суммирующая предыдущий результат операций с текущий элементом. Принимает в качестве аргумента вспомогательный объект и следующий элемент потока.
  3. combiner - если стрим работает в многопоточном режиме, то эта функция служит для агрегирования результатов выполненных в разных потоках.
  4. finisher возвращает итоговый результат операций.
  5. characteristics возвращает характеристики реализуемого класса. Существует несколько характеристик, их стоит перечислить:
    • CONCURRENT возможность выполнения в разных потоках, если она указана, то функция combiner должна иметь осмысленную реализацию.
    • UNORDERED указывает, что результат выполнения не сохраняет порядок входящих элементов, т.е. если мы на вход получим отсортированный поток, то на выходе будет не отсортированный, или имеющий измененный порядок.
    • IDENTITY_FINISH указывает что функция finisher может быть пропущена, перед получением итогового результата.

Приступим к реализации. Для того, чтобы было более понятно, начнем с вспомогательного класса.

Думаю, здесь все очевидно и трудностей в понимании кода не возникнет.
Теперь сам коллектор. Пааарааам.

Как мы видим по характеристикам, коллектор работает в многопоточной среде и результат его работы не сохраняет порядок элементов.
Собственно все. Теперь можно использовать, например, так:

Результат выполнения будет следующий: