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.