Автор нашей собственной Мачей Миклас , 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
Приятного сбора!