Розуміння Spliterator, Collector та Stream на Java 8


143

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

Що саме таке a Spliteratorі a Collector, і як я можу їх використовувати? Якщо я готовий написати своє Spliteratorчи Collector(і, мабуть, своє Streamв цьому процесі), що мені робити, а що не робити?

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

Відповіді:


142

Ви майже напевно не повинні мати справу з Spliteratorкористувачем; це повинно бути необхідно лише в тому випадку, якщо ви самі пишете Collectionтипи, а також маєте намір оптимізувати паралельні операції над ними.

Оскільки це варте, а Spliterator- це спосіб роботи над елементами колекції таким чином, що легко розділити частину колекції, наприклад, тому що ви паралельно паралелізуєте і хочете, щоб одна нитка працювала над однією частиною колекції, одна нитка для роботи над іншою частиною тощо.

По суті, ніколи не слід зберігати значення типу Streamу змінній. Streamна зразок подібного до того Iterator, що це об'єкт одноразового використання, який ви майже завжди будете використовувати у плавному ланцюжку, як у прикладі Javadoc:

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

Collectorє найбільш узагальненою, абстрактною можливою версією операції "зменшити" a la map / redu; зокрема, він повинен підтримувати кроки паралелізації та завершення. Приклади Collectors включають:

  • підбиття підсумків, напр Collectors.reducing(0, (x, y) -> x + y)
  • StringBuilder, що додається, наприклад Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)

31
Spliterator також пропонує спосіб передавати Iterable, який не є колекцією
Bohemian

2
Я мав на увазі "скоротити операцію, в тому сенсі, що термін означає в карті / зменшити"
Луї Васерман

1
Чи Collectors.ofбуло видалено старий метод бета-версії, або я щось пропускаю? Для повноти (x,y) -> x+yможна записати як Integer::sum.
Жан-Франсуа Савард

3
Е, ні, вибачте, це Collector.of, а не Collectors.of.
Луї Вассерман

2
Ваш приклад колекціонера був би кориснішим, якби ви пояснили, що робить кожен з ваших колекціонерів.
MiguelMunoz

90

Spliterator в основному означає "розбивний ітератор".

Один потік може пройти / обробити весь Spliterator сам, але Spliterator також має метод, trySplit()який "розщепить" розділ для когось іншого (як правило, інший потік) обробляти, залишаючи поточний сплітератор менше роботи.

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

Наприклад, найпростіший колектор мав би початкове значення 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
Це не відповідає на запитання ОП, плюс немає пояснення чи опису вашої публікації.
Сид

4

Інтерфейс Spliterator- це основна особливість потоків .

Методи 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. В іншому випадку, return false- це означає, що немає елемента або кінця потоку.

  • Spliterator<T> trySplit() Цей метод дозволяє розділити набір даних на багато менших наборів відповідно до тих чи інших критеріїв (розмір файлу, кількість рядків тощо).


"Якщо операція виконана успішно." Ви, ймовірно, повинні переробити це. tryAdvance javadoc зрозуміліше: "Якщо залишився елемент, виконує задану дію над ним, повертаючи істину; інше повертає помилково. '
Піро каже, що
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.