1. Overview
In this quick tutorial, we’ll explore various ways to find the maximum value in a Java Map. We’ll also see how new features in Java 8 have simplified this operation.
Before we begin let’s briefly recap how objects are compared in Java.
Typically objects can express a natural ordering by implementing the method compareTo() from the Comparable interface. However, an ordering other than natural ordering can be employed through a Comparator object. We’ll see these in more details as we go on.
2. Before Java 8
Let’s start first exploring how can we find the highest value without Java 8 features.
2.1. Using Simple Iteration
Using iteration we could simply go through all the entries of a Map to pick the highest value, storing the current highest in a variable:
public <K, V extends Comparable<V>> V maxUsingIteration(Map<K, V> map) {
Map.Entry<K, V> maxEntry = null;
for (Map.Entry<K, V> entry : map.entrySet()) {
if (maxEntry == null || entry.getValue()
.compareTo(maxEntry.getValue()) > 0) {
maxEntry = entry;
}
}
return maxEntry.getValue();
}
Here, we’re also making use of Java generics to build a method that can be applied to different types.
2.2. Using Collections.max()
Now let’s see how the utility method max() in the Collections class can save us from writing a lot of this ourselves:
public <K, V extends Comparable<V>> V maxUsingCollectionsMax(Map<K, V> map) {
Entry<K, V> maxEntry = Collections.max(map.entrySet(), new Comparator<Entry<K, V>>() {
public int compare(Entry<K, V> e1, Entry<K, V> e2) {
return e1.getValue()
.compareTo(e2.getValue());
}
});
return maxEntry.getValue();
}
In this example, we’re passing a Comparator object to max() which can leverage the natural ordering of the Entry values through compareTo() or implement a different ordering altogether.
3. After Java 8
Java 8 features can simplify our attempt above to get the max value from a Map in more ways than one.
3.1. Using Collections.max() with a Lambda Expression
Let’s begin by exploring how lambda expressions can simplify the call to Collections.max():
public <K, V extends Comparable<V>> V maxUsingCollectionsMaxAndLambda(Map<K, V> map) {
Entry<K, V> maxEntry = Collections.max(map.entrySet(), (Entry<K, V> e1, Entry<K, V> e2) -> e1.getValue()
.compareTo(e2.getValue()));
return maxEntry.getValue();
}
As we can see here, lambda expressions save us from defining the full-fledged functional interface and provide a concise way of defining the logic. To read more about lambda expressions, also check out our previous article.
3.2. Using Stream
The Stream API is another addition to Java 8 which has largely simplified working with collections:
public <K, V extends Comparable<V>> V maxUsingStreamAndLambda(Map<K, V> map) {
Optional<Entry<K, V>> maxEntry = map.entrySet()
.stream()
.max((Entry<K, V> e1, Entry<K, V> e2) -> e1.getValue()
.compareTo(e2.getValue())
);
return maxEntry.get().getValue();
}
This API offers a lot of data processing queries like map-reduce transformations on collections. Here, we’ve used max() over a stream of Map Entry which is a special case of a reduction operation. More details about the Stream API are available here.
We’re also making use of the Optional API here which is a container object added in Java 8 that may or may not contain a non-null value. More details about Optional can be obtained here.
3.3. Using Stream with Method Reference
Lastly, let’s see how method references can further simplify our use of lambda expressions:
public <K, V extends Comparable<V>> V maxUsingStreamAndMethodReference(Map<K, V> map) {
Optional<Entry<K, V>> maxEntry = map.entrySet()
.stream()
.max(Comparator.comparing(Map.Entry::getValue));
return maxEntry.get()
.getValue();
}
In cases where lambda expressions are merely calling an existing method, a method reference allows us to do this using the method name directly. For more details about method references have a look at this previous article.
4. Conclusion
In this article, we’ve seen multiple ways of finding the highest value in a Java Map, some of which were using features added as part of Java 8.
As always, the code for the examples is available over on GitHub.