Overview
We often have to compute aggregates over a collection of records, that maybe count users by per day, max order size across all the orders, or min number of customer ratings across all the orders delivered.
If you are using java, there are various ways to compute these aggregates including traditional style with imperative style or more modern way with Java streams API.
In this article let’s explore various ways to compute these aggregates across list of numbers as an example.
Imperative style
This style is the traditional way of computing aggregates across records. we use for each loop to iterate over each record and compare it with the current max/min value and then by the end of the loop our min/max variable is finally updated with the result that we want.
List<Integer> numbers = List.of(1, 2, 3, 4);
int max = Integer.MIN_VALUE;
int min = Integer.MIN_VALUE;
// iterative approach
for(int num:numbers){
if(num>max){
max = num;
}
}
Java Collections API
Java Collections API also provide method such as max() and min()
// Collections api
List<Integer> numbers = List.of(1, 2, 3, 4);
int max = Collections.max(numbers);
int min = Collections.min(numbers);
Java 8 and Beyond
- After the release of Java 8, we have many ways to calculate aggregates using streams API.
Max() & Min() Over Stream
- we can use stream API and use max() and min() operation to achieve max/min value.
// Java streams api
List<Integer> numbers = List.of(1, 2, 3, 4);
int max =numbers.stream().max(Comparator.comparing(i->i)).get();
int min =numbers.stream().min(Comparator.comparing(i->i)).get();
Reduce over Stream
- Another way is to use reduce operation and perform max/min using java Math API.
// Java streams api
List<Integer> numbers = List.of(1, 2, 3, 4);
int max = numbers.stream().reduce(Math::max).get();
int min = numbers.stream().reduce(Math::min).get();
Map over Stream
- If we want to calculate average and count we can map to int or double to calculate average or count.
// Java streams api
List<Integer> numbers = List.of(1, 2, 3, 4);
int max = numbers.stream().reduce(Integer::max).get();
int min = numbers.stream().reduce(Integer::min).get();
double average = numbers.stream().mapToDouble(i->i).average().getAsDouble();
long count = numbers.stream().mapToDouble(i->i).count();
Using Collectors API
- Collectors API provides a bunch of methods to calculate max/min/average and count.
int max = numbers.stream().collect(Collectors.maxBy(Comparator.naturalOrder())).get();
int min = numbers.stream().collect(Collectors.minBy(Comparator.naturalOrder())).get();
double average = numbers.stream().collect(Collectors.averagingDouble(a->a)).doubleValue();
long count = numbers.stream().collect(Collectors.counting());
- Additionally Collectors API also provides summarizing method that provides aggregates as output results.
double average = numbers.stream().collect(Collectors.summarizingInt(a->a)).getAverage();
int max = numbers.stream().collect(Collectors.summarizingInt(a->a)).getMax();
int count = numbers.stream().collect(Collectors.summarizingInt(a->a)).getCount();
int min = numbers.stream().collect(Collectors.summarizingInt(a->a)).getMin();
Conclusion
- In this article, we discussed various methods that as a java developer we can use to calculate aggregates such as min/max/count/average over the list of numbers.
- We can of course use these methods in the collection of any objects.
- Java streams provide a more readable approach over imperative style code to calculate aggregates.
Bonus Tip
- If you want to upskill your Java, you should definitely check out this bestseller course