What are the different ways to group a collection in Java?

Let’s say you have a collection of phones.

The phones have the attributes : model name , phone type (Android or Iphone) , cost , color and rating.

And you want to group the phones based on whether they are Iphones or Android Phones.

You want to find the costliest IPhone and the costliest Android phone.

You want to find the average , minimum and maximum prices for Iphones and the same for Android phones.

You want the total cost of all Iphones and the total cost of all Android Phones.

Java 8 provides a very simple way to calculate all these.

The method Collectors.groupingBy() helps in achieving this.

Let’s dive into the code:

Here is the Phone class:

package groupingby;

public class Phone {

	String model;

	protected PhoneType phoneType;

	Integer cost;

	String color;

	int rating;

	public Phone(String model, PhoneType phoneType, Integer cost, String color, int rating) {
		super();
		this.model = model;
		this.phoneType = phoneType;
		this.cost = cost;
		this.color = color;
		this.rating = rating;
	}

	public String getModel() {
		return model;
	}

	public void setModel(String model) {
		this.model = model;
	}

	public PhoneType getPhoneType() {
		return phoneType;
	}

	public void setPhoneType(PhoneType phoneType) {
		this.phoneType = phoneType;
	}

	public Integer getCost() {
		return cost;
	}

	public void setCost(Integer cost) {
		this.cost = cost;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public int getRating() {
		return rating;
	}

	public void setRating(int rating) {
		this.rating = rating;
	}

	@Override
	public String toString() {
		return "Phone [model=" + model + ", phoneType=" + phoneType + ", cost=" + cost + ", color=" + color
				+ ", rating=" + rating + "]";
	}

}

Here is the PhoneType enum used inside the Phone class:

package groupingby;

public enum PhoneType {

	ANDROID,

	IPHONE

}

Here are the phones in the collection:

Phone phone1 = new Phone("Iphone SE", PhoneType.IPHONE, 30000, "white", 8);

		Phone phone2 = new Phone("Samsung Galaxy A", PhoneType.ANDROID, 25000, "grey", 7);

		Phone phone3 = new Phone("Micromax T", PhoneType.ANDROID, 10000, "yellow", 6);

		Phone phone4 = new Phone("Iphone 6", PhoneType.IPHONE, 40000, "red", 8);

		Phone phone5 = new Phone("Iphone X", PhoneType.IPHONE, 100000, "silver", 9);

		Phone phone6 = new Phone("Google Pixel", PhoneType.ANDROID, 60000, "white", 7);

		Phone phone7 = new Phone("Samsung Gold", PhoneType.ANDROID, 80000, "grey", 8);

		Phone phone8 = new Phone("Micromax E", PhoneType.ANDROID, 15000, "blue", 7);

		Phone phone9 = new Phone("Iphone 5", PhoneType.IPHONE, 15000, "white", 6);

		Phone phone10 = new Phone("Samsung S", PhoneType.ANDROID, 50000, "silver", 8);

		List<Phone> phones = new ArrayList<Phone>();

		phones.add(phone1);
	        
                phones.add(phone2);

		phones.add(phone3);

		phones.add(phone4);

		phones.add(phone5);

		phones.add(phone6);

		phones.add(phone7);

		phones.add(phone8);

		phones.add(phone9);

		phones.add(phone10);

Grouping by phone type:

Let’s first group all the phones by their types.

This can be done by the below one line of code:

Map<PhoneType, List<Phone>> phonesByType = phones.stream().collect(Collectors.groupingBy(Phone::getPhoneType));

We are passing a method reference (Phone::getPhoneType) as a parameter to groupingBy() .

The resultant Map is a map with PhoneType as key and list of Phone as values.

Let’s print them using System.out.println():

		System.out.println("Android phones:"+phonesByType.get(PhoneType.ANDROID));

		System.out.println("Iphones: "+phonesByType.get(PhoneType.IPHONE));

Here is the output:

Android phones:[Phone [model=Samsung Galaxy A, phoneType=ANDROID, cost=25000, color=grey, rating=7], Phone [model=Micromax T, phoneType=ANDROID, cost=10000, color=yellow, rating=6], Phone [model=Google Pixel, phoneType=ANDROID, cost=60000, color=white, rating=7], Phone [model=Samsung Gold, phoneType=ANDROID, cost=80000, color=grey, rating=8], Phone [model=Micromax E, phoneType=ANDROID, cost=15000, color=blue, rating=7], Phone [model=Samsung S, phoneType=ANDROID, cost=50000, color=silver, rating=8]]
Iphones: [Phone [model=Iphone SE, phoneType=IPHONE, cost=30000, color=white, rating=8], Phone [model=Iphone 6, phoneType=IPHONE, cost=40000, color=red, rating=8], Phone [model=Iphone X, phoneType=IPHONE, cost=100000, color=silver, rating=9], Phone [model=Iphone 5, phoneType=IPHONE, cost=15000, color=white, rating=6]]

Grouping by phone type and using only the model names

What if we want just the model name of the Phones after grouping by phone type.

This can be achieved using the below code:

	Map<PhoneType, List<String>> phoneNamesByType = phones.stream().collect(
				Collectors.groupingBy(Phone::getPhoneType, Collectors.mapping(Phone::getModel, Collectors.toList())));


The second argument to Collectors.groupingBy() method in the above case is a mapping function which picks the model names of the phones and stores them in a list.

Let’s print the output:

	System.out.println("Android phones:"+phoneNamesByType.get(PhoneType.ANDROID));

		
      System.out.println("Iphones:"+phoneNamesByType.get(PhoneType.IPHONE));

Here is the output:

Android phones:[Samsung Galaxy A, Micromax T, Google Pixel, Samsung Gold, Micromax E, Samsung S]
Iphones:[Iphone SE, Iphone 6, Iphone X, Iphone 5]

Finding total cost of all phones of each type:

Now,

Let’s find the total cost of all Iphones and the total cost of all Android phones.

This can be done using the below code:


		Map<PhoneType, Integer> phoneCostByType = phones.stream().collect(
				Collectors.groupingBy(Phone::getPhoneType, Collectors.reducing(0, Phone::getCost, Integer::sum)));

In the above case , the second argument to the groupingBy() method is a reducing function() which takes an initial value of 0, picks the cost value of each phone and applies sum operation to all of them.

Let’s print the output:

	System.out.println("Android phones cost:" + phoneCostByType.get(PhoneType.ANDROID));

		System.out.println("Iphones cost:" + phoneCostByType.get(PhoneType.IPHONE));

Here is the output:

Android phones cost:240000
Iphones cost:185000

Finding statistics of the cost of each phone type:

If you want to find the minimum,maximum,total and average cost of each phone type , you can do it using the below code:

	Map<PhoneType, IntSummaryStatistics> phoneStatisticsByType = phones.stream()
				.collect(Collectors.groupingBy(Phone::getPhoneType, Collectors.summarizingInt(Phone::getCost)));

IntSummaryStatistics is a built in class in Java which facilitates this. In the above case the second argument to the groupingBy() method is a Collector – summarizingInt. It summarizes based on the cost.

Let’s print the output:

System.out.println("Android phones statistics based on cost:" + phoneStatisticsByType.get(PhoneType.ANDROID));

		System.out.println("Iphones statistics based on cost:" + phoneStatisticsByType.get(PhoneType.IPHONE));

Here is the output:

Android phones statistics based on cost:IntSummaryStatistics{count=6, sum=240000, min=10000, average=40000.000000, max=80000}
Iphones statistics based on cost:IntSummaryStatistics{count=4, sum=185000, min=15000, average=46250.000000, max=100000}

The costliest IPhone costs 100000 rupees and the costliest Andriod phone costs 80000 rupees. The average cost of Android phones is 40000 rupees and the average cost of Iphones is 46250 rupees. So much data from a single line of code!

Finding costliest phone of each type:

Finally lets find the costliest phone of each type.

This can be done using the below code:

Map<PhoneType, Optional<Phone>> phonesByMaxCost = phones.stream().collect(
				Collectors.groupingBy(Phone::getPhoneType, Collectors.maxBy(Comparator.comparingInt(Phone::getCost))));

The second argument to groupingBy() method in the above case takes a Collector – maxBy() which finds the maximum of the phone cost and returns the corresponding Phone.

Let’s print the output:

	System.out.println("Costliest android phone:" + phonesByMaxCost.get(PhoneType.ANDROID).get());

		System.out.println("Costliest Iphone:" + phonesByMaxCost.get(PhoneType.IPHONE).get());
	

And here is the output:

Costliest android phone:Phone [model=Samsung Gold, phoneType=ANDROID, cost=80000, color=grey, rating=8]
Costliest Iphone:Phone [model=Iphone X, phoneType=IPHONE, cost=100000, color=silver, rating=9]

The costliest Iphone in our collection is Iphone X and it costs a lakh rupees.

That’s it.

The above all scenarios show the power of Collectors.groupingBy() method.

And much more can be done using it!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s