«1. Обзор

В этом кратком руководстве мы рассмотрим различные способы нахождения максимального значения в карте Java. Мы также увидим, как новые возможности Java 8 упростили эту операцию.

Прежде чем мы начнем, давайте кратко вспомним, как объекты сравниваются в Java.

Обычно объекты могут выражать естественный порядок, реализуя метод compareTo() из интерфейса Comparable. Однако порядок, отличный от естественного, может использоваться через объект Comparator. Мы увидим их более подробно по ходу дела.

2. До Java 8

Давайте сначала начнем изучать, как мы можем найти наибольшее значение без функций Java 8.

2.1. Использование простой итерации

Используя итерацию, мы могли бы просто пройтись по всем записям Map, чтобы выбрать самое высокое значение, сохранив текущее максимальное значение в переменной:

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();
}

Здесь мы также используем дженерики Java. построить метод, который можно применять к разным типам.

2.2. Использование Collections.max()

Теперь давайте посмотрим, как служебный метод max() в классе Collections может избавить нас от написания большей части этого самостоятельно:

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();
}

В этом примере мы передаем Comparator объект в max(), который может использовать естественный порядок значений Entry через compareTo() или вообще реализовать другой порядок.

3. После Java 8

Возможности Java 8 могут упростить нашу попытку получить максимальное значение из Map несколькими способами.

3.1. Использование Collections.max() с лямбда-выражением

Давайте начнем с изучения того, как лямбда-выражения могут упростить вызов 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();
}

Как мы видим здесь, лямбда-выражения избавляют нас от определения полноценный функциональный интерфейс и краткий способ определения логики. Чтобы узнать больше о лямбда-выражениях, также ознакомьтесь с нашей предыдущей статьей.

3.2. Использование Stream

Stream API — это еще одно дополнение к Java 8, которое значительно упростило работу с коллекциями:

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();
}

Этот API предлагает множество запросов на обработку данных, таких как преобразование map-reduce в коллекциях. Здесь мы использовали max() для потока Map Entry, что является частным случаем операции сокращения. Более подробная информация о Stream API доступна здесь.

Здесь мы также используем необязательный API, который представляет собой объект-контейнер, добавленный в Java 8, который может содержать или не содержать ненулевое значение. Более подробную информацию о факультативе можно получить здесь.

3.3. Использование Stream со ссылкой на метод

Наконец, давайте посмотрим, как ссылки на методы могут еще больше упростить использование лямбда-выражений:

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();
}

В случаях, когда лямбда-выражения просто вызывают существующий метод, ссылка на метод позволяет нам сделать это используя имя метода напрямую. Дополнительные сведения о ссылках на методы см. в этой предыдущей статье.

4. Заключение

В этой статье мы рассмотрели несколько способов нахождения наибольшего значения в карте Java, некоторые из которых использовали функции, добавленные как часть Java 8.

Как всегда, код примеры доступны на GitHub.