I have a simple Customer class like so
public class Customer {
public int age;
public int discount;
public String name;
public Customer(String name) {
this.name = name;
}
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Customer(String name, int age, int discount) {
this.name = name;
this.age = age;
this.discount = discount;
}
@Override
public String toString() {
return "Customer [age=" + age + ", discount=" + discount + ", name=" + name + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Integer getDiscount() {
return discount;
}
public void setDiscount(int discount) {
this.discount = discount;
}
}
I populate a list of these objects using this
List<Customer> customerList = new ArrayList<>(Arrays.asList(
new Customer("John", 2, 15),
new Customer("John", 4, 15),
new Customer("John", 6, 25),
new Customer("Joe", 3, 15),
new Customer("Joe", 3, 15),
new Customer("Joe", 3, 15),
new Customer("Goerge", 6, 25),
new Customer("Goerge", 6, 25),
new Customer("Mary", 7, 25),
new Customer("Jane", 1, 15),
new Customer("Jane", 2, 15),
new Customer("Jane", 8, 25),
new Customer("Jane", 8, 25)
));
Now I want to group and count the names and discounts, using a collector like this
Map<Object, Long> collected = customerList
.stream()
.collect(Collectors.groupingBy(x -> Arrays.asList(x.name, x.discount), Collectors.counting()));
I can review my output using this
collected.entrySet().forEach(c -> {
System.out.println(c);
});
Which outputs the following
[Jane, 15]=2
[Joe, 15]=3
[John, 15]=2
[Mary, 25]=1
[John, 25]=1
[Jane, 25]=2
[Goerge, 25]=2
The question is how do I sort the Map by name and discount so it looks like this
[Goerge, 25]=2
[Jane, 15]=2
[Jane, 25]=2
[Joe, 15]=3
[John, 15]=2
[John, 25]=1
[Mary, 25]=1
I keep bumping up against the Object type that is returned by the collector?
Can I cast the collector so that it returns a class, maybe something like
private class DiscountCounts
{
public String name;
public Integer discount;
}
Is it possible to convert the Map<**Object**, Long>() to something like Map<DiscountCounts, Long>(), would this allow access to the fields of the Map key using lambda or Comparator constructs?
I tried something like this, iterate over the map and manually convert to the Map I want but I can't get to the original collection's keys?
Map<DiscountCounts, Long> collected2 = new HashMap<>();
collected.entrySet().forEach(o -> {
DiscountCounts key1 = (DiscountCounts)o.getKey(); //--> Fails here
collected2.put((DiscountCounts)o.getKey(), o.getValue());
});
One way you can do it without using DiscountCounts class is, first sort the list and then perform the groping by operation, and use LinkedHashMap to save the sorted order
Map<List<Object>, Long> map = customerList.stream()
.sorted(Comparator.comparing(Customer::getName).thenComparing(Customer::getDiscount))
.collect(Collectors.groupingBy(x -> Arrays.asList(x.name, x.discount),LinkedHashMap::new, Collectors.counting()));
The another way using DiscountCounts class is, by override the equals and hashcode of DiscountCounts class and do a groupingBy creating DiscountCounts object for every Customer object as key in Map and use TreeMap with Comparator to sort the result
Map<DiscountCounts, Long> result = customerList.stream().collect(Collectors.groupingBy(
c -> new DiscountCounts(c.getName(), c.getDiscount()),
() -> new TreeMap<DiscountCounts, Long>(
Comparator.comparing(DiscountCounts::getName).thenComparing(DiscountCounts::getDiscount)),
Collectors.counting()));
@Andreas suggest in the comment enlighten me another way of doing it, and i feel this is one of the best approach you can implement Comparable on DiscountCounts and provide the sorting logic so that you don't need to provide Comparator to TreeMap
@Override
public int compareTo(DiscountCounts cust) {
int last = this.getName().compareTo(cust.getName());
return last == 0 ? this.getDiscount().compareTo(cust.getDiscount()) : last;
}
Map<DiscountCounts, Long> result1 = customerList.stream().collect(Collectors.groupingBy(
c -> new DiscountCounts(c.getName(), c.getDiscount()), TreeMap::new, Collectors.counting()));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With