1. 序章

このチュートリアルでは、 Java Streams を使用してMap sを操作する方法の例について説明します。 これらの演習のいくつかは、双方向の Map データ構造を使用して解決できることは注目に値しますが、ここでは機能的なアプローチに関心があります。

最初に、マップおよびストリームを操作するために使用する基本的な考え方について説明します。 次に、マップに関連するいくつかの異なる問題と、ストリームを使用したそれらの具体的なソリューションを紹介します。

2. 基本的な考え方

注意すべき主な点は、 Stream は、Collectionから簡単に取得できる要素のシーケンスであるということです。

Maps は異なる構造を持ち、シーケンスなしでキーから値へのマッピングがあります。 ただし、これは、 Map 構造を別のシーケンスに変換して、StreamAPIを自然に操作できるようにすることができないという意味ではありません。

マップからさまざまなコレクションを取得する方法を見てみましょう。これをストリームにピボットできます。

Map<String, Integer> someMap = new HashMap<>();

キーと値のペアのセットを取得できます。

Set<Map.Entry<String, Integer>> entries = someMap.entrySet();

マップに関連付けられたキーセットを取得することもできます。

Set<String> keySet = someMap.keySet();

または、値のセットを直接操作することもできます。

Collection<Integer> values = someMap.values();

これらはそれぞれ、それらからストリームを取得することによってそれらのコレクションを処理するためのエントリポイントを提供します。

Stream<Map.Entry<String, Integer>> entriesStream = entries.stream();
Stream<Integer> valuesStream = values.stream();
Stream<String> keysStream = keySet.stream();

3. Streamを使用してMapのキーを取得する

3.1. 入力データ

マップがあると仮定しましょう:

Map<String, String> books = new HashMap<>();
books.put(
"978-0201633610", "Design patterns : elements of reusable object-oriented software");
books.put(
  "978-1617291999", "Java 8 in Action: Lambdas, Streams, and functional-style programming");
books.put("978-0134685991", "Effective Java");

「EffectiveJava」というタイトルの本のISBNを見つけることに興味があります。

3.2. 一致の取得

マップに本のタイトルが存在しなかったため、関連するISBNがないことを示したいと思います。 オプションのを使用して、次のことを表現できます。

この例では、そのタイトルに一致する本のキーに関心があると仮定します。

Optional<String> optionalIsbn = books.entrySet().stream()
  .filter(e -> "Effective Java".equals(e.getValue()))
  .map(Map.Entry::getKey)
  .findFirst();

assertEquals("978-0134685991", optionalIsbn.get());

コードを分析してみましょう。 最初に、前に見たように、MapからentrySetを取得します。

タイトルに「EffectiveJava」が含まれるエントリのみを検討するため、最初の中間操作はfilterになります。

マップエントリ全体ではなく、各エントリのキーに関心があります。したがって、次の連鎖中間操作はまさにそれを行います。これは、マップ操作であり、出力としての新しいストリーム。これには、探していたタイトルに一致するエントリのキーのみが含まれます。

1つの結果のみが必要なため、findFirst()ターミナル操作を適用できます。これにより、Streamの初期値がOptionalオブジェクトとして提供されます。

タイトルが存在しない場合を見てみましょう。

Optional<String> optionalIsbn = books.entrySet().stream()
  .filter(e -> "Non Existent Title".equals(e.getValue()))
  .map(Map.Entry::getKey).findFirst();

assertEquals(false, optionalIsbn.isPresent());

3.3. 複数の結果を取得する

次に、問題を変更して、1つではなく複数の結果を返す方法を確認しましょう。

複数の結果を返すには、次の本をマップに追加しましょう。

books.put("978-0321356680", "Effective Java: Second Edition");

したがって、「Effective Java」で始まるすべてのの本を探すと、複数の結果が返されます。

List<String> isbnCodes = books.entrySet().stream()
  .filter(e -> e.getValue().startsWith("Effective Java"))
  .map(Map.Entry::getKey)
  .collect(Collectors.toList());

assertTrue(isbnCodes.contains("978-0321356680"));
assertTrue(isbnCodes.contains("978-0134685991"));

この場合、フィルター条件を置き換えて、 String の同等性を比較するのではなく、Mapの値が「EffectiveJava」で始まるかどうかを確認します。

今回は最初のものを選ぶのではなく、結果を収集し、一致するものをリストに入れます。

4. Streamを使用してMapの値を取得する

次に、マップに関する別の問題に焦点を当てましょう。 タイトルに基づいてISBNを取得する代わりに、ISBNに基づいてタイトルを取得しようとします。

オリジナルのマップを使用してみましょう。 「978-0」で始まるISBNのタイトルを検索します。

List<String> titles = books.entrySet().stream()
  .filter(e -> e.getKey().startsWith("978-0"))
  .map(Map.Entry::getValue)
  .collect(Collectors.toList());

assertEquals(2, titles.size());
assertTrue(titles.contains(
  "Design patterns : elements of reusable object-oriented software"));
assertTrue(titles.contains("Effective Java"));

この解決策は、以前の一連の問題の解決策に似ています。 エントリセットをストリーミングしてから、フィルタリング、マッピング、および収集します。

また、以前と同様に、最初の一致のみを返したい場合は、 map メソッドの後で、にすべての結果を収集する代わりに、 findFirst()メソッドを呼び出すことができます。リスト

5. 結論

この記事では、 地図機能的な方法で 。

特に、関連するコレクションを Map に使用するように切り替えると、Streamを使用した処理がはるかに簡単で直感的になることがわかりました。

もちろん、この記事のすべての例は、GitHubプロジェクトにあります。