Понимание 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 является наиболее обобщенной, абстрактно возможной версией операции "уменьшить" а-ля сопоставление / уменьшение; в частности, он должен поддерживать этапы распараллеливания и завершения. Примеры Collectors включают в себя:

  • суммирование, например 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));
Другие вопросы по тегам