Есть ли метод 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>
.