Есть ли метод aggregateBy в потоке Java 8 api?

Бегите по этой очень интересной, но год назад презентация Брайана Гетца - в слайде он представляет метод aggregateBy(), предположительно в Stream API, который должен объединять элементы списка (?) с картой (учитывая начальное значение по умолчанию и метод, управляющий значением (для дубликатов ключей также) - см. следующий слайд в презентации).

По-видимому, такого метода нет в Stream API. Есть ли другой метод, который делает что-то аналогичное в Java 8?

Ответы

Ответ 1

Операцию агрегации можно выполнить с помощью класса Collectors. Таким образом, в видеоролике пример будет эквивалентен:

Map<String, Integer> map = 
    documents.stream().collect(Collectors.groupingBy(Document::getAuthor, Collectors.summingInt(Document::getPageCount)));

Метод groupingBy даст вам Map<String, List<Document>>. Теперь вам нужно использовать сборщик нисходящего потока, чтобы суммировать все количество страниц для каждого документа в List, связанных с каждым ключом.

Это делается путем предоставления нисходящему коллектору groupingBy, который равен summingInt, что приводит к Map<String, Integer>.

<ч/" > Они дают в основном тот же пример в документации, где они вычисляют сумму заработной платы сотрудников отделом.

Я думаю, что они удалили эту операцию и создали класс Collectors вместо этого, чтобы иметь полезный класс, который содержит много сокращений, которые вы обычно будете использовать.

Ответ 2

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

Существует несколько способов сделать это, и вы можете, например, использовать коллекцию toMap для агрегирования данных для отдела:

  • первым аргументом является ключ-манипулятор (ваша ось агрегации = отдел),
  • второй - это блок отображения значений (данные, которые вы хотите скомпилировать = зарплаты), и
  • третья - функция слияния (как вы хотите агрегировать данные = суммировать значения).

Пример:

import static java.util.stream.Collectors.*;

public static void main(String[] args) {
  List<Person> persons = Arrays.asList(new Person("John", "Sales", 10000),
                                       new Person("Helena", "Sales", 10000),
                                       new Person("Somebody", "Marketing", 15000));

  Map<String, Double> salaryByDepartment = persons.stream()
          .collect(toMap(Person::department, Person::salary, (s1, s2) -> s1 + s2));
  System.out.println("salary by department = " + salaryByDepartment);
}

Как и в случае с потоками, существует несколько способов получить желаемый результат, например:

import static java.util.stream.Collectors.*;

Map<String, Double> salaryByDepartment = persons.stream()
        .collect(groupingBy(Person::department, summingDouble(Person::salary)));

Для справки класс Person:

static class Person {
  private final String name, department;
  private final double salary;
  public Person(String name, String department, double salary) {
    this.name = name;
    this.department = department;
    this.salary = salary;
  }
  public String name() { return name; }
  public String department() { return department; }
  public double salary() { return salary; }
}

Ответ 3

Эта конкретная запись Javadoc - это самая близкая вещь, которую я смог найти в этой части агрегации на Java 8. Несмотря на то, что это сторонний API, подписи, похоже, довольно хорошо выстраиваются - вы предоставляете некоторую функцию для получения значений, какую-либо функцию терминала для значений (нуль в этом случае) и некоторую функцию для объединения функции и значений вместе.

Он очень похож на Collector, который предоставит нам возможность сделать это.

Map<String, Integer> strIntMap =
    intList.stream()
           .collect(Collectors
               .groupingBy(Document::getAuthor,
                Collectors.summingInt(Document::getPageCount)));

Тогда идея состоит в том, что мы группируем имя автора для каждой записи в нашем списке и добавляем общие номера страниц, которые автор имеет в Map<String, Integer>.