Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

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.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.