Понимание Spliterator, Collector и Stream в Java 8

144

У меня возникают проблемы понимания Streamинтерфейса в Java 8, особенно там , где он должен делать с Spliteratorи Collectorинтерфейсов. Моя проблема в том, что я пока просто не могу понять Spliteratorи Collectorинтерфейсы, и в результате Streamинтерфейс для меня все еще несколько неясен.

Что такое «а» Spliteratorи «а» Collectorи как их использовать? Если я хочу написать свое собственное Spliteratorили Collector(и, возможно, свое собственное Streamв этом процессе), что мне делать и не делать?

Я прочитал несколько примеров, разбросанных по сети, но поскольку здесь все еще новое и может быть изменено, примеров и руководств по-прежнему очень мало.

Виктор Стафуса
источник

Ответы:

143

Вам почти наверняка никогда не придется иметь дело с Spliteratorпользователем; это должно быть необходимо только в том случае, если вы Collectionсами пишете типы и также намереваетесь оптимизировать распараллеленные операции с ними.

Как бы то ни было, a Spliterator- это способ работы с элементами коллекции таким образом, чтобы можно было легко отделить часть коллекции, например, потому что вы распараллеливаете и хотите, чтобы один поток работал с одной частью коллекции, один поток для работы над другой частью и т. д.

По сути, вы также никогда не должны сохранять значения типа Streamв переменную. Streamв некотором роде похож на объект Iterator, поскольку это одноразовый объект, который вы почти всегда будете использовать в плавной цепочке, как в примере Javadoc:

int sum = widgets.stream()
                  .filter(w -> w.getColor() == RED)
                  .mapToInt(w -> w.getWeight())
                  .sum();

Collectorявляется наиболее обобщенной, абстрактной возможной версией операции «сокращение» a la map / reduce; в частности, он должен поддерживать этапы распараллеливания и завершения. Примеры Collectorвключают:

  • суммирование, например Collectors.reducing(0, (x, y) -> x + y)
  • Добавление StringBuilder, например Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)
Луи Вассерман
источник
31
Spliterator (s) также предоставляет способ потоковой передачи Iterable, который не является коллекцией
Bohemian
2
Я имел в виду «операцию сокращения в том смысле, в каком этот термин используется в map / reduce»
Луи Вассерман
1
Есть Collectors.ofстарый метод бета - версии , которая была удалена или я-то отсутствует? Для полноты (x,y) -> x+yкартины можно записать как Integer::sum.
Jean-François Savard
3
Эээ, нет, извините, это Collector.of, а не Collectors.of.
Луи Вассерман
2
Ваш пример Collector был бы более полезным, если бы вы объяснили, что делает каждый из ваших Collector.
MiguelMunoz
91

Spliterator в основном означает «разделяемый итератор».

Одиночный поток может проходить / обрабатывать весь Spliterator сам, но Spliterator также имеет метод, trySplit()который « отделяет » раздел для обработки другим (обычно другим потоком), оставляя текущий разделитель с меньшими затратами.

Collectorобъединяет спецификацию reduceфункции (известности, уменьшающей карту), с начальным значением и функцией для объединения двух результатов (что позволяет объединить результаты из разделенных потоков работы).

Например, самый простой Collector будет иметь начальное значение 0, добавлять целое число к существующему результату и «объединять» два результата, добавляя их. Таким образом суммируется разделенный поток целых чисел.

Увидеть:

Томас В
источник
значение для объединения двух результатов?
Джейсон Лоу,
@JasonLaw - уточнил! Спасибо за предложение.
Thomas W
5

Ниже приведены примеры использования предопределенных коллекторов для выполнения общих задач изменяемого сокращения:

 // 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));
Аджай Кумар
источник
2
Это не отвечает на вопрос Op, плюс нет объяснения или описания вашего сообщения.
Сид
5

Интерфейс 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() Этот метод позволяет разбить набор данных на множество меньших наборов по тем или иным критериям (размер файла, количество строк и т. Д.).


источник
«Если операция выполнена успешно…» Вам, наверное, стоит перефразировать это. tryAdvance javadoc более понятен: «Если оставшийся элемент существует, выполняет над ним заданное действие, возвращая истину; иначе возвращает false. »
Пиро говорит:« Восстановите Монику »