Понимание Spliterator, Collector и Stream в Java 8
У меня проблемы с пониманием Stream
интерфейс в Java 8, особенно там, где это связано с Spliterator
а также Collector
интерфейсы. Моя проблема в том, что я просто пока не могу понять Spliterator
и Collector
интерфейсы, и в результате Stream
Интерфейс все еще несколько неясен для меня.
Что именно Spliterator
и Collector
и как я могу их использовать? Если я готов написать свой собственный Spliterator
или же Collector
(и, вероятно, мой собственный Stream
в этом процессе), что я должен делать, а не делать?
Я прочитал несколько примеров, разбросанных по всему Интернету, но, поскольку все здесь все еще новое и подвержено изменениям, примеры и учебные пособия все еще очень редки.
4 ответа
Вы почти наверняка никогда не должны иметь дело с Spliterator
как пользователь; это должно быть необходимо, только если вы пишете Collection
печатает сам, а также намеревается оптимизировать параллельные операции над ними.
Для чего это стоит, Spliterator
это способ работы с элементами коллекции таким образом, что легко отделить часть коллекции, например, потому что вы распараллеливаете и хотите, чтобы один поток работал в одной части коллекции, один поток работал в другой часть и т. д.
По сути, вы никогда не должны сохранять значения типа Stream
к переменной, либо. Stream
вроде как Iterator
в том смысле, что это объект одноразового использования, который вы почти всегда будете использовать в плавной цепочке, как в примере Javadoc:
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();
Collector
является наиболее обобщенной, абстрактно возможной версией операции "уменьшить" а-ля сопоставление / уменьшение; в частности, он должен поддерживать этапы распараллеливания и завершения. Примеры Collector
s включают в себя:
- суммирование, например
Collectors.reducing(0, (x, y) -> x + y)
- Добавление StringBuilder, например
Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)
Spliterator
в основном означает "разделяемый итератор".
Один поток может проходить / обрабатывать весь Spliterator, но Spliterator также имеет метод trySplit()
который будет "отделять" раздел для обработки другим (обычно другим потоком), оставляя текущий сплитератор с меньшими затратами труда.
Collector
сочетает в себе спецификацию reduce
функция (карты с уменьшением славы) с начальным значением и значением для объединения двух результатов (что позволяет объединять результаты из разделенных потоков работы).
Например, самый простой Collector будет иметь начальное значение 0, добавить целое число к существующему результату и "объединит" два результата, добавив их. Таким образом, суммируя поток целых чисел.
Увидеть:
Spliterator.trySplit()
Collector<T,A,R>
Интерфейс Spliterator
- это ключевая особенность Streams.
stream()
а также parallelStream()
Методы по умолчанию представлены в Collection
интерфейс. Эти методы используют Spliterator через вызов к spliterator()
:
...
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
...
Spliterator - это внутренний итератор, который разбивает поток на более мелкие части. Эти меньшие части могут обрабатываться параллельно.
Среди других методов есть два наиболее важных для понимания Spliterator:
boolean tryAdvance(Consumer<? super T> action)
в отличие отIterator
, он пытается выполнить операцию со следующим элементом. Если операция выполнена успешно, метод возвращаетtrue
, В противном случае возвращаетfalse
- это означает, что отсутствует элемент или конец потока.Spliterator<T> trySplit()
Этот метод позволяет разбить набор данных на множество меньших наборов по тем или иным критериям (размер файла, количество строк и т. Д.).
Ниже приведены примеры использования предопределенных сборщиков для выполнения распространенных задач изменения изменчивости:
// Accumulate names into a List
List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
// Accumulate names into a TreeSet
Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));
// Convert elements to strings and concatenate them, separated by commas
String joined = things.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
// Compute sum of salaries of employee
int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));
// Group employees by department
Map<Department, List<Employee>> byDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// Compute sum of salaries by department
Map<Department, Integer> totalByDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
// Partition students into passing and failing
Map<Boolean, List<Student>> passingFailing =
students.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));