1. 概要

このチュートリアルでは、Guavaを使用してコレクションをフィルタリングおよび変換する方法を説明します。

述語を使用してフィルタリングし、ライブラリが提供する関数を使用して変換し、最後に、フィルタリングと変換の両方を組み合わせる方法を説明します。

2. コレクションをフィルタリングする

コレクションのフィルタリングの簡単な例から始めましょう。 ライブラリによって提供され、Predicatesユーティリティクラスを介して構築されたすぐに使用可能な述語を使用します。

@Test
public void whenFilterWithIterables_thenFiltered() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Iterable<String> result 
      = Iterables.filter(names, Predicates.containsPattern("a"));

    assertThat(result, containsInAnyOrder("Jane", "Adam"));
}

ご覧のとおり、名前の List をフィルタリングして、文字「a」を含む名前のみを取得しています。また、 Iterables.filter()を使用して実行しています。それ。

または、 Collections2.filter()APIをうまく利用することもできます。

@Test
public void whenFilterWithCollections2_thenFiltered() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<String> result 
      = Collections2.filter(names, Predicates.containsPattern("a"));
    
    assertEquals(2, result.size());
    assertThat(result, containsInAnyOrder("Jane", "Adam"));

    result.add("anna");
    assertEquals(5, names.size());
}

ここで注意すべき点がいくつかあります。まず、 Collections.filter()の出力は元のコレクションのライブビューです。一方への変更はもう一方に反映されます。

結果は述語によって制約されることを理解することも重要です。述語を満たさない要素を追加すると、IllegalArgumentExceptionは投げられる:

@Test(expected = IllegalArgumentException.class)
public void givenFilteredCollection_whenAddingInvalidElement_thenException() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<String> result 
      = Collections2.filter(names, Predicates.containsPattern("a"));

    result.add("elvis");
}

3. カスタムフィルターの書き込み述語

次へ–ライブラリが提供するものを使用する代わりに、独自の述語を作成しましょう。 次の例では、「A」または「J」で始まる名前のみを取得する述語を定義します。

@Test
public void whenFilterCollectionWithCustomPredicate_thenFiltered() {
    Predicate<String> predicate = new Predicate<String>() {
        @Override
        public boolean apply(String input) {
            return input.startsWith("A") || input.startsWith("J");
        }
    };

    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<String> result = Collections2.filter(names, predicate);

    assertEquals(3, result.size());
    assertThat(result, containsInAnyOrder("John", "Jane", "Adam"));
}

4. 複数の述語を組み合わせる

Predicates.or()および Predicates.and()を使用して、複数の述語を組み合わせることができます。 次の例では、名前の List をフィルタリングして、「J」で始まる名前または「a」を含まない名前を取得します。

@Test
public void whenFilterUsingMultiplePredicates_thenFiltered() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<String> result = Collections2.filter(names, 
      Predicates.or(Predicates.containsPattern("J"), 
      Predicates.not(Predicates.containsPattern("a"))));

    assertEquals(3, result.size());
    assertThat(result, containsInAnyOrder("John", "Jane", "Tom"));
}

5. コレクションのフィルタリング中にヌル値を削除する

次の例のように、コレクションから null値をPredicates.notNull()でフィルタリングすることにより、クリーンアップできます。

@Test
public void whenRemoveNullFromCollection_thenRemoved() {
    List<String> names = 
      Lists.newArrayList("John", null, "Jane", null, "Adam", "Tom");
    Collection<String> result = 
      Collections2.filter(names, Predicates.notNull());

    assertEquals(4, result.size());
    assertThat(result, containsInAnyOrder("John", "Jane", "Adam", "Tom"));
}

6. コレクション内のすべての要素が条件に一致するかどうかを確認します

次に、コレクション内のすべての要素が特定の条件に一致するかどうかを確認しましょう。 Iterables.all()を使用して、すべての名前に「n」または「m」が含まれているかどうかを確認してから、すべての要素に「a」が含まれているかどうかを確認します。

@Test
public void whenCheckingIfAllElementsMatchACondition_thenCorrect() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

    boolean result = Iterables.all(names, Predicates.containsPattern("n|m"));
    assertTrue(result);

    result = Iterables.all(names, Predicates.containsPattern("a"));
    assertFalse(result);
}

7. コレクションを変革する

では、Guava関数を使用してコレクションを変換する方法を見てみましょう。 次の例では、名前の ListIterables.transform()[を使用して Integers (名前の長さ)のListに変換します。 X156X]:

@Test
public void whenTransformWithIterables_thenTransformed() {
    Function<String, Integer> function = new Function<String, Integer>() {
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    };

    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Iterable<Integer> result = Iterables.transform(names, function);

    assertThat(result, contains(4, 4, 4, 3));
}

次の例のように、 Collections2.transform()APIを使用することもできます。

@Test
public void whenTransformWithCollections2_thenTransformed() {
    Function<String,Integer> func = new Function<String,Integer>(){
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    };

    List<String> names = 
      Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<Integer> result = Collections2.transform(names, func);

    assertEquals(4, result.size());
    assertThat(result, contains(4, 4, 4, 3));

    result.remove(3);
    assertEquals(3, names.size());
}

Collections.transform()の出力は元のコレクションのライブビューであることに注意してください。一方に変更を加えると、もう一方に影響します。

そして、以前と同じように、出力 Collection に要素を追加しようとすると、UnsupportedOperationExceptionがスローされます。

8. 述語から関数を作成します

Functions.fromPredicate()を使用して、PredicateからFunctionを作成することもできます。 もちろん、これは、述語の条件に従って、入力をブール値に変換する関数になります。

次の例では、名前の List をブール値のリストに変換します。ここで、各要素は、名前に「m」が含まれているかどうかを表します。

@Test
public void whenCreatingAFunctionFromAPredicate_thenCorrect() {
    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<Boolean> result =
      Collections2.transform(names,
      Functions.forPredicate(Predicates.containsPattern("m")));

    assertEquals(4, result.size());
    assertThat(result, contains(false, false, true, true));
}

9. 2つの機能の構成

次へ–合成された関数を使用してコレクションを変換する方法を見てみましょう。

Functions.compose()は、最初のFunctionの出力に2番目のFunctionを適用するため、2つの関数の合成を返します。

次の例では、最初の Function が名前をその長さに変換し、次に2番目のFunctionが長さをboolean値に変換します。偶数です:

@Test
public void whenTransformingUsingComposedFunction_thenTransformed() {
    Function<String,Integer> f1 = new Function<String,Integer>(){
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    };

    Function<Integer,Boolean> f2 = new Function<Integer,Boolean>(){
        @Override
        public Boolean apply(Integer input) {
            return input % 2 == 0;
        }
    };

    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<Boolean> result = 
      Collections2.transform(names, Functions.compose(f2, f1));

    assertEquals(4, result.size());
    assertThat(result, contains(true, true, true, false));
}

10. フィルタリングと変換を組み合わせる

そして今、Guavaが持っているもう1つのクールなAPIを見てみましょう。これにより、フィルタリングと変換をチェーンでつなぐことができます。FluentIterable

次の例では、名前の List をフィルタリングし、FluentIterableを使用して変換します。

@Test
public void whenFilteringAndTransformingCollection_thenCorrect() {
    Predicate<String> predicate = new Predicate<String>() {
        @Override
        public boolean apply(String input) {
            return input.startsWith("A") || input.startsWith("T");
        }
    };

    Function<String, Integer> func = new Function<String,Integer>(){
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    };

    List<String> names = Lists.newArrayList("John", "Jane", "Adam", "Tom");
    Collection<Integer> result = FluentIterable.from(names)
                                               .filter(predicate)
                                               .transform(func)
                                               .toList();

    assertEquals(2, result.size());
    assertThat(result, containsInAnyOrder(4, 3));
}

場合によっては、命令型バージョンの方が読みやすく、機能的アプローチよりも優先される必要があることに注意してください。

11. 結論

最後に、Guavaを使用してコレクションをフィルタリングおよび変換する方法を学びました。 フィルタリングにはCollections2.filter() Iterables.filter() API、および Collections2.transform()Iterablesを使用しました。コレクションを変換するためのtransform()

最後に、フィルタリングと変換の両方を組み合わせるための非常に興味深いFluentIterable流暢なAPIについて簡単に説明しました。

これらすべての例とコードスニペットの実装は、GitHubプロジェクトにあります。これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。