Автор нашей собственной Мачей Миклас , Lean Java Expert @comSysto
Java 8 поставляется с потоковым API, он разделяет обработку данных на две фазы: промежуточные операции и терминальные операции. Терминальная операция может иметь несколько различных форм, и я хотел бы сосредоточиться на сокращении до коллекций, особенно до неизменных коллекций Guava. Для работы терминала требуется сборщик, который будет собирать данные и возвращать их в виде требуемой структуры, но Guava не предоставляет такой сборщик. Чтобы создать коллекцию Guava из потока, мы должны сначала уменьшить результат потока во временную коллекцию и затем передать его:
import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toList; import com.google.common.collect.ImmutableSortedSet; ...stream.map(...).filter(....). collect(collectingAndThen(Collectors.toList(), ImmutableSortedSet::copyOf));
Сокращение нашего потока хранит результаты во временном списке (Collectors.toList ()). По завершении потоковой обработки функция финишера преобразует содержимое этого списка в коллекцию Guava (ImmutableSortedSet :: copyOf).
Проблема этого подхода заключается в том, что… у нас есть дополнительный цикл преобразования и два массива в памяти (List и Builder). Этого можно было бы избежать, если бы у нас был коллектор, построенный на основе Guava Builder. Так…. Я реализовал один, как только мы его используем, приведенный выше код можно упростить до такой формы:
import static org.cyclop.common.Gullectors.toNaturalImmutableSortedSet; import com.google.common.collect.ImmutableSortedSet; ...stream.map(...).filter(....).collect( toNaturalImmutableSortedSet());
Код прост, давайте сосредоточимся на реализации #toNaturalImmutableSortedSet ()
public static <T extends Comparable<?>> Collector<T, ?, ImmutableSortedSet<T>> toNaturalImmutableSortedSet() { Supplier<ImmutableSortedSet.Builder<T>> supplier = ImmutableSortedSet::naturalOrder; BiConsumer<ImmutableSortedSet.Builder<T>, T> accumulator = (b, v) -> b.add(v); BinaryOperator<ImmutableSortedSet.Builder<T>> combiner = (l, r) -> l.addAll(r.build()); Function<ImmutableSortedSet.Builder<T>, ImmutableSortedSet<T>> finisher = ImmutableSortedSet.Builder::build; return Collector.of(supplier, accumulator, combiner, finisher); }
Наш коллектор создается фабричным методом Collector #, который принимает четыре аргумента:
- # Поставщик — эта функция будет вызываться только один раз , чтобы создать структуру , которая будет собирать результаты потоков — в нашем случае это полный редактор из ImmutableSortedSet
- # аккумулятор — предоставляет функцию, которая будет выполняться для каждого элемента, который достигает терминальной операции, то есть каждый элемент, который прошел через поток и должен быть собран для возврата. В нашем случае мы предоставляем функцию, которая будет выполнять #add (v) в Builder, которая была указана в первом аргументе (# supplier )
- # combiner — этот пример не будет использоваться в нашем примере, но он необходим для обработки параллельных потоков, он будет использоваться для их объединения
- # Финишер — это последний шаг, и он будет выполнен после обработки потока. Элементы, возвращаемые потоком, содержатся в Builder (# supplier ), и на этом последнем этапе мы вызываем для него метод # build () , что приводит к ImmutableSortedSet !
На основе этого шаблона мы можем реализовать другие сборщики:
public static <T> Collector<T, ?, ImmutableList<T>> toImmutableList() { Supplier<ImmutableList.Builder<T>> supplier = ImmutableList.Builder::new; BiConsumer<ImmutableList.Builder<T>, T> accumulator = (b, v) -> b.add(v); BinaryOperator<ImmutableList.Builder<T>> combiner = (l, r) -> l.addAll(r.build()); Function<ImmutableList.Builder<T>, ImmutableList<T>> finisher = ImmutableList.Builder::build; return Collector.of(supplier, accumulator, combiner, finisher); } public static <T> Collector<T, ?, ImmutableSet<T>> toImmutableSet() { Supplier<ImmutableSet.Builder<T>> supplier = ImmutableSet.Builder::new; BiConsumer<ImmutableSet.Builder<T>, T> accumulator = (b, v) -> b.add(v); BinaryOperator<ImmutableSet.Builder<T>> combiner = (l, r) -> l.addAll(r.build()); Function<ImmutableSet.Builder<T>, ImmutableSet<T>> finisher = ImmutableSet.Builder::build; return Collector.of(supplier, accumulator, combiner, finisher); } public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap( Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) { Supplier<ImmutableMap.Builder<K, V>> supplier = ImmutableMap.Builder::new; BiConsumer<ImmutableMap.Builder<K, V>, T> accumulator = (b, t) -> b.put(keyMapper.apply(t), valueMapper.apply(t)); BinaryOperator<ImmutableMap.Builder<K, V>> combiner = (l, r) -> l.putAll(r.build()); Function<ImmutableMap.Builder<K, V>, ImmutableMap<K, V>> finisher = ImmutableMap.Builder::build; return Collector.of(supplier, accumulator, combiner, finisher); }
Наконец, вот исходный код: Gullectors.java и модульные тесты: TestGullectors.java
Приятного сбора!