Javaでコレクションをフィルタリングする方法
1. 概要
この短いチュートリアルでは、we’ll have a look at different ways of filtering a Collection in Java –つまり、特定の条件を満たすすべてのアイテムを検索します。
これは、事実上すべてのJavaアプリケーションに存在する基本的なタスクです。
このため、この目的のための機能を提供するライブラリの数は膨大です。
特に、このチュートリアルでは以下について説明します。
-
Java 8 Streamsのfilter()関数
-
Java 9filteringコレクター
-
関連するEclipse Collections API
-
ApacheのCollectionUtils filter()メソッド
-
グアバのCollections2 filter()アプローチ
2. ストリームを使用する
Java 8が導入されて以来、データのコレクションを処理する必要があるほとんどの場合、Streamsが重要な役割を果たしています。
したがって、これはJavaで構築され、追加の依存関係を必要としないため、ほとんどの場合に推奨されるアプローチです。
2.1. Streamsを使用したコレクションのフィルタリング
簡単にするために、Integerの値のin all the examples our objective will be to create a method that retrieves only the even numbers from a Collection。
したがって、各アイテムを評価するために使用する条件を「value % 2 == 0」として表すことができます。
いずれの場合も、この条件をPredicateオブジェクトとして定義する必要があります。
public Collection findEvenNumbers(Collection baseCollection) {
Predicate streamsPredicate = item -> item % 2 == 0;
return baseCollection.stream()
.filter(streamsPredicate)
.collect(Collectors.toList());
}
each library we analyze in this tutorial provides its own Predicate implementationに注意することが重要ですが、それでも、それらはすべて関数型インターフェースとして定義されているため、Lambda関数を使用して宣言することができます。
この場合、要素をListに蓄積する、Javaによって提供される事前定義されたCollector を使用しましたが、this previous postで説明されているように、他のものを使用することもできます。
2.2. Java 9でコレクションをグループ化した後のフィルタリング
ストリームを使用すると、groupingBy collectorを使用してアイテムを集約できます。
ただし、前のセクションで行ったようにフィルタリングすると、このコレクターが登場する前に、いくつかの要素が初期段階で破棄される可能性があります。
このため、the filtering collector was introduced with Java 9, with the objective of processing the subcollections after they have been grouped.
この例に従って、奇数を除外する前に、各整数の桁数に基づいてコレクションをグループ化するとします。
public Map> findEvenNumbersAfterGrouping(
Collection baseCollection) {
Function getQuantityOfDigits = item -> (int) Math.log10(item) + 1;
return baseCollection.stream()
.collect(groupingBy(
getQuantityOfDigits,
filtering(item -> item % 2 == 0, toList())));
}
つまり、このコレクターを使用すると、値のエントリが空になる可能性がありますが、グループ化する前にフィルタリングすると、コレクターはそのようなエントリをまったく作成しません。
もちろん、要件に基づいてアプローチを選択します。
3. Eclipse Collectionsの使用
アプリケーションがJava8をサポートしていないため、またはJavaが提供していない強力な機能を利用したい場合は、他のサードパーティライブラリを利用して目的を達成することもできます。
これは、Eclipse Collectionsの場合です。これは、新しいパラダイムに対応し、最新のすべてのJavaリリースによって導入された変更を進化させて受け入れるように努めるライブラリです。
まず、Eclipse Collections Introductory postを調べて、このライブラリが提供する機能についてより幅広い知識を得ることができます。
3.1. 依存関係
プロジェクトのpom.xmlに次の依存関係を追加することから始めましょう。
org.eclipse.collections
eclipse-collections
9.2.0
eclipse-collectionsには、必要なすべてのデータ構造インターフェースとAPI自体が含まれます。
3.2. Eclipse Collectionsを使用したコレクションのフィルタリング
次に、MutableListなどのデータ構造の1つでEclipseのフィルタリング機能を使用してみましょう。
public Collection findEvenNumbers(Collection baseCollection) {
Predicate eclipsePredicate
= item -> item % 2 == 0;
Collection filteredList = Lists.mutable
.ofAll(baseCollection)
.select(eclipsePredicate);
return filteredList;
}
別の方法として、Iterateのselect() staticメソッドを使用してfilteredListオブジェクトを定義することもできます。
Collection filteredList
= Iterate.select(baseCollection, eclipsePredicate);
4. ApacheのCollectionUtilsを使用する
ApacheのCollectionUtilsライブラリの使用を開始するために、その使用法について説明したthis short tutorialを確認できます。
ただし、このチュートリアルでは、 filter()の実装に焦点を当てます。
4.1. 依存関係
まず、pom.xmlファイルに次の依存関係が必要です。
org.apache.commons
commons-collections4
4.2
4.2. CollectionUtilsを使用したコレクションのフィルタリング
これで、CollectonUtilsのメソッドを使用する準備が整いました。
public Collection findEvenNumbers(Collection baseCollection) {
Predicate apachePredicate = item -> item % 2 == 0;
CollectionUtils.filter(baseCollection, apachePredicate);
return baseCollection;
}
このメソッドは、条件に一致しないすべてのアイテムを削除することによってbaseCollectionを変更することを考慮に入れる必要があります。
これは、the base Collection has to be mutable, otherwise it will throw an exceptionを意味します。
5. GuavaのCollections2を使用する
以前と同様に、このテーマの詳細については、以前の投稿‘Filtering and Transforming Collections in Guava'を読むことができます。
5.2. Collections2を使用したコレクションのフィルタリング
ご覧のとおり、このアプローチは最後のセクションで説明したアプローチとかなり似ています。
public Collection findEvenNumbers(Collection baseCollection) {
Predicate guavaPredicate = item -> item % 2 == 0;
return Collections2.filter(baseCollection, guavaPredicate);
}
ここでも、Guava固有のPredicateオブジェクトを定義します。
この場合、GuavaはbaseCollectionを変更せず、新しいコレクションを生成するため、不変のコレクションを入力として使用できます。
6. 結論
要約すると、Javaでコレクションをフィルタリングする方法はたくさんあることがわかりました。
通常、Streamsが推奨されるアプローチですが、他のライブラリが提供する機能を知って覚えておくと便利です。
特に、古いJavaバージョンをサポートする必要がある場合。 ただし、この場合、ラムダなどのチュートリアル全体で使用されている最近のJava機能を匿名クラスに置き換える必要があることに留意する必要があります。
いつものように、このチュートリアルに示されているすべての例は、Github repoにあります。