Java group by function

Stream group by using Java

This post will look at some common uses of stream groupingBy . We will cover grouping by single and multiple fields, counting the elements in a group and filtering on group size. We also cover some basic statistics you can use with groupingBy .

Grouping elements in a stream

The examples in this post will use the Fruit class you can see below. The equals and hashCode methods are overridden, so two instances of the Fruit class will be equal if they have the same name . The toString method is overridden to have a more readable output in the console. The getters and setters are left out to make it more readable.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 
class Fruit  String name; int amount; public Fruit(String name, int amount)  this.name = name; this.amount = amount; > // Getters and setters @Override public String toString() return name + "Object";> @Override public boolean equals(Object o)  if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Fruit fruit = (Fruit) o; return Objects.equals(name, fruit.name); > @Override public int hashCode()  return Objects.hash(name);> > 

This is the list of fruits we will use for the examples.

ListFruit> listOfFruits = Arrays.asList( new Fruit("Apple", 1), new Fruit("Apple", 3), new Fruit("Banana", 2), new Fruit("Banana", 2), new Fruit("Banana", 5), new Fruit("Pear", 0), new Fruit("Orange", 4) ); 

Stream group by fields

Let’s start with grouping by one field. The example below will group the fruits by their name. The result is a Map with the fruit’s name as key and a list of fruits as the value.

MapString, ListFruit>> collect = listOfFruits .stream() .collect(Collectors.groupingBy( Fruit::getName )); 

If you print collect to the console it will show the following result. First the key (the fruits name) is printed followed by the key’s value a list of fruit objects.

Apple=[AppleObject, AppleObject], Pear=[PearObject], Orange=[OrangeObject], Banana=[BananaObject, BananaObject, BananaObject]> 

Stream group by multiple fields

This example looks very familiar to the first example. We only added a new Collectors.groupingBy() as the second parameter to the groupingBy . The List is now first grouped by the fruit’s name and then by the fruit’s amount.

MapString, MapInteger, ListFruit>>> collect = listOfFruits .stream() .collect(Collectors.groupingBy( Fruit::getName, Collectors.groupingBy(Fruit::getAmount) )); 

When you print the Map the result will look like this:

Apple=1=[AppleObject], 3=[AppleObject]>, Pear=0=[PearObject]>, Orange=4=[OrangeObject]>, Banana=2=[BananaObject, BananaObject], 5=[BananaObject]>> 

Stream group by count

We can also pass the Collectors.counting() as a parameter to the groupingBy . This collector will give you the number of objects inside the group. The result of this example is a map with the fruit’s name as the key and the count as the value.

MapString, Long> collect = listOfFruits() .stream() .collect(Collectors.groupingBy( fruit -> fruit.name, Collectors.counting() )); 

When you print the Map , you see a group for each fruit name followed by the number of fruits in that group.

Apple=2, Pear=1, Orange=1, Banana=3> 

Stream group by count and filter on count

We can take the previous example further by filtering on the count. To filter on the count, we need to create a new stream of the entrySet of the map and apply the filter we want. So in the example below, we filter out all the groups that are smaller than two.

MapString, Long> collect = listOfFruits() .stream() .collect(Collectors.groupingBy( Fruit::getName, Collectors.counting() )) .entrySet() .stream() .filter(fruitLongEntry -> fruitLongEntry.getValue() >= 2) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 

When you print the Map , we see that only apple and banana have groups size of two or more.

Stream group by sum

We can also sum all the fields in a group. To do that, you need to pass the Collectors.summingInt() to the groupingBy .

MapFruit, Integer> collect = listOfFruitsa .stream() .collect(Collectors.groupingBy( fruit -> fruit, Collectors.summingInt(f -> f.amount) )); 

When you print the Map , you can see that the amount of the fruits is summed up for each group.

Apple=4, Pear=0, Orange=4, Banana=9> 

Stream group by and summarize (count, sum, min, avg, max)

It is also possible to get all the basic statistics when you do a groupingBy . You only have to pass the Collectors.summarizingInt as the second parameter to get these statistics. There is also a summarizingDouble and summarizingLong .

MapFruit, IntSummaryStatistics> collect = listOfFruits .stream() .collect(Collectors.groupingBy( fruit -> fruit, Collectors.summarizingInt(f -> f.amount) )); 

When you print the map you can see the count, sum, min, average, and max value for each of the groups.

Apple=IntSummaryStatisticscount=2, sum=4, min=1, average=2.000000, max=3>, Pear=IntSummaryStatisticscount=1, sum=0, min=0, average=0.000000, max=0>, Orange=IntSummaryStatisticscount=1, sum=4, min=4, average=4.000000, max=4>, Banana=IntSummaryStatisticscount=3, sum=9, min=2, average=3.000000, max=5>> 

Conclusion

We implemented different kinds of groupingBy in this post. We saw how to group by one or more fields, get the size of the groups and to get some basic statistics.

Источник

Java group by function

Чтобы сгруппировать данные по какому-нибудь признаку, нам надо использовать в связке метод collect() объекта Stream и метод Collectors.groupingBy() . Допустим, у нас есть следующий класс:

class Phone < private String name; private String company; private int price; public Phone(String name, String comp, int price)< this.name=name; this.company=comp; this.price = price; >public String getName() < return name; >public int getPrice() < return price; >public String getCompany() < return company; >>

И, к примеру, у нас есть набор объектов Phone, которые мы хотим сгруппировать по компании:

import java.util.List; import java.util.Map; import java.util.stream.Stream; import java.util.stream.Collectors; public class Program < public static void main(String[] args) < StreamphoneStream = Stream.of(new Phone("iPhone X", "Apple", 600), new Phone("Pixel 2", "Google", 500), new Phone("iPhone 8", "Apple",450), new Phone("Galaxy S9", "Samsung", 440), new Phone("Galaxy S8", "Samsung", 340)); Map phonesByCompany = phoneStream.collect( Collectors.groupingBy(Phone::getCompany)); for(Map.Entry item : phonesByCompany.entrySet()) < System.out.println(item.getKey()); for(Phone phone : item.getValue())< System.out.println(phone.getName()); >System.out.println(); > > >
Google Pixel 2 Apple iPhone X iPhone 8 Samsung Galaxy S9 Galaxy S8

Итак, для создания групп в метод phoneStream.collect() передается вызов функции Collectors.groupingBy() , которая с помощью выражения Phone::getCompany группирует объекты по компании. В итоге будет создан объект Map, в котором ключами являются названия компаний, а значениями — список связанных с компаниями телефонов.

Метод Collectors.partitioningBy

Метод Collectors.partitioningBy() имеет похожее действие, только он делит элементы на группы по принципу, соответствует ли элемент определенному условию. Например:

Map> phonesByCompany = phoneStream.collect( Collectors.partitioningBy(p->p.getCompany()=="Apple")); for(Map.Entry> item : phonesByCompany.entrySet()) < System.out.println(item.getKey()); for(Phone phone : item.getValue())< System.out.println(phone.getName()); >System.out.println(); >

В данном случае с помощью условия p->p.getCompany()==»Apple» мы смотрим, принадлежит ли телефон компании Apple. Если телефон принадлежит этой компании, то он попадает в одну группу, если нет, то в другую.

Метод Coollectors.counting

Метод Collectors.counting применяется в Collectors.groupingBy() для вычисления количества элементов в каждой группе:

Map phonesByCompany = phoneStream.collect( Collectors.groupingBy(Phone::getCompany, Collectors.counting())); for(Map.Entry item : phonesByCompany.entrySet())

Google -1 Apple - 2 Samsung - 2

Метод Collectors.summing

Метод Collectors.summing применяется для подсчета суммы. В зависимости от типа данных, к которым применяется метод, он имеет следующие формы: summingInt() , summingLong() , summingDouble() . Применим этот метод для подсчета стоимости всех смартфонов по компаниям:

Map phonesByCompany = phoneStream.collect( Collectors.groupingBy(Phone::getCompany, Collectors.summingInt(Phone::getPrice))); for(Map.Entry item : phonesByCompany.entrySet())

С помощью выражения Collectors.summingInt(Phone::getPrice)) мы указываем, что для каждой компании будет вычислять совокупная цена всех ее смартфонов. И поскольку вычисляется результат — сумма для значений типа int, то в качестве типа возвращаемой коллекции используется тип Map

Google - 500 Apple - 1050 Samsung - 780

Методы maxBy и minBy

Методы maxBy и minBy применяются для подсчета минимального и максимального значения в каждой группе. В качестве параметра эти методы принимают функцию компаратора, которая нужна для сравнения значений. Например, найдем для каждой компании телефон с минимальной ценой:

Map> phonesByCompany = phoneStream.collect( Collectors.groupingBy(Phone::getCompany, Collectors.minBy(Comparator.comparing(Phone::getPrice)))); for(Map.Entry> item : phonesByCompany.entrySet())

Google - Pixel 2 Apple - iPhone 8 Samsung - Galaxy S8

В качестве возвращаемого значения операции группировки используется объект Map> . Опять же поскольку группируем по компаниям, то ключом будет выступать строка, а значением — объект Optional .

Метод summarizing

Методы summarizingInt() / summarizingLong() / summarizingDouble() позволяют объединить в набор значения соответствующих типов:

import java.util.IntSummaryStatistics; //. Map priceSummary = phoneStream.collect( Collectors.groupingBy(Phone::getCompany, Collectors.summarizingInt(Phone::getPrice))); for(Map.Entry item : priceSummary.entrySet())

Метод Collectors.summarizingInt(Phone::getPrice)) создает набор, в который помещаются цены для всех телефонов каждой из групп. Данный набор инкапсулируется в объекте IntSummaryStatistics . Соответственно если бы мы применяли методы summarizingLong() или summarizingDouble() , то соответственно бы получали объекты LongSummaryStatistics или DoubleSummaryStatistics.

У этих объектов есть ряд методов, который позволяют выполнить различные атомарные операции над набором:

  • getAverage() : возвращает среднее значение
  • getCount() : возвращает количество элементов в наборе
  • getMax() : возвращает максимальное значение
  • getMin() : возвращает минимальное значение
  • getSum() : возвращает сумму элементов
  • accept() : добавляет в набор новый элемент

В данном случае мы получаем среднюю цену смартфонов для каждой группы.

Google - 500.0 Apple - 525.0 Samsung - 390.0

Метод mapping

Метод mapping позволяет дополнительно обработать данные и задать функцию отображения объектов из потока на какой-нибудь другой тип данных. Например:

Map> phonesByCompany = phoneStream.collect( Collectors.groupingBy(Phone::getCompany, Collectors.mapping(Phone::getName, Collectors.toList()))); for(Map.Entry> item : phonesByCompany.entrySet()) < System.out.println(item.getKey()); for(String name : item.getValue())< System.out.println(name); >>

Выражение Collectors.mapping(Phone::getName, Collectors.toList()) указывает, что в группу будут выделятся названия смартфонов, причем группа будет представлять объект List.

Источник

Читайте также:  Index php убрать чпу
Оцените статью