1. 概要

このクイックチュートリアルでは、 JavaMapで最大値を見つけるためのさまざまな方法を探ります。 また、 Java8の新機能によってこの操作がどのように簡素化されたかについても説明します。

始める前に、オブジェクトがJavaでどのように比較されるかを簡単に要約しましょう。

通常、オブジェクトは、 CompareableインターフェイスからメソッドcompareTo()を実装することにより、自然な順序を表現できます。 ただし、コンパレータオブジェクトを介して、自然な順序以外の順序を使用できます。 これらについては、今後さらに詳しく見ていきます。

2. Java8より前

まず、Java8の機能なしで最高の価値を見つける方法を探り始めましょう。

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()の使用

次に、 Collectionsクラスのユーティリティメソッドmax()を使用して、これを自分で作成する手間を省くことができます。

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()に渡します。これにより、 compareTo()を介して Entry 値の自然な順序を利用したり、まったく異なる順序。

3. Java8以降

Java 8の機能を使用すると、Mapから最大値を取得するための上記の試みを1つ以上の方法で簡略化できます。

3.1. Collections.max()をLambda式で使用する

ラムダ式が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 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変換のような多くのデータ処理クエリを提供します。 ここでは、縮小操作の特殊なケースであるMap Entry のストリームに対してmax()を使用しました。 Stream APIの詳細については、こちらをご覧ください。

ここでは、オプション APIも使用しています。これは、Java 8で追加されたコンテナオブジェクトであり、null以外の値が含まれる場合と含まれない場合があります。 オプションの詳細については、こちらをご覧ください。

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

ラムダ式が単に既存のメソッドを呼び出している場合、メソッド参照を使用すると、メソッド名を直接使用してこれを行うことができます。 m ethodリファレンスの詳細については、この前の記事を参照してください。

4. 結論

この記事では、 Javaマップで最大値を見つける複数の方法を見てきました。そのうちのいくつかは、Java8の一部として追加された機能を使用していました。

いつものように、例のコードはGitHubから入手できます。