java.util.Collection
述語に基づいてフィルタリングしたい。
29 に答える
Java 8 ( 2014 ) は、1 行のコードでストリームとラムダを使用してこの問題を解決します。
List<Person> beerDrinkers = persons.stream()
.filter(p -> p.getAge() > 16).collect(Collectors.toList());
ここにチュートリアルがあります。
Collection#removeIf
コレクションをその場で変更するために使用します。(注意: この場合、述語は述語を満たすオブジェクトを削除します):
persons.removeIf(p -> p.getAge() <= 16);
lambdajを使用すると、ループや内部クラスを記述せずにコレクションをフィルタリングできます。
List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
greaterThan(16)));
もっと読みやすいものを想像できますか?
免責事項:私は lambdaj の寄稿者です
Java 1.5を使用していて、 Google Collectionsを追加できないと仮定すると、Google の担当者が行ったことと非常によく似たことを行います。これは、Jon のコメントのわずかなバリエーションです。
まず、このインターフェースをコードベースに追加します。
public interface IPredicate<T> { boolean apply(T type); }
その実装者は、特定の述語が特定のタイプで真である場合に応答できます。たとえば、 wasとimplementsの場合、渡さT
れたものが承認されているかどうかを返します。User
AuthorizedUserPredicate<User>
IPredicate<T>
AuthorizedUserPredicate#apply
User
次に、いくつかのユーティリティクラスで、次のように言うことができます
public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
Collection<T> result = new ArrayList<T>();
for (T element: target) {
if (predicate.apply(element)) {
result.add(element);
}
}
return result;
}
したがって、上記を使用していると仮定すると、
Predicate<User> isAuthorized = new Predicate<User>() {
public boolean apply(User user) {
// binds a boolean method in User to a reference
return user.isAuthorized();
}
};
// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);
線形チェックのパフォーマンスが問題になる場合は、ターゲット コレクションを持つドメイン オブジェクトが必要になることがあります。ターゲット コレクションを持つドメイン オブジェクトには、ターゲット コレクションを初期化、追加、および設定するメソッドのフィルタリング ロジックがあります。
アップデート:
ユーティリティ クラス (Predicate としましょう) では、述語が期待値を返さない場合のデフォルト値のオプションと、新しい IPredicate 内で使用される params の静的プロパティを備えた select メソッドを追加しました。
public class Predicate {
public static Object predicateParams;
public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
Collection<T> result = new ArrayList<T>();
for (T element : target) {
if (predicate.apply(element)) {
result.add(element);
}
}
return result;
}
public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
T result = null;
for (T element : target) {
if (!predicate.apply(element))
continue;
result = element;
break;
}
return result;
}
public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
T result = defaultValue;
for (T element : target) {
if (!predicate.apply(element))
continue;
result = element;
break;
}
return result;
}
}
次の例では、コレクション間で欠落しているオブジェクトを探します。
List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
new IPredicate<MyTypeA>() {
public boolean apply(MyTypeA objectOfA) {
Predicate.predicateParams = objectOfA.getName();
return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
public boolean apply(MyTypeB objectOfB) {
return objectOfB.getName().equals(Predicate.predicateParams.toString());
}
}) == null;
}
});
次の例では、コレクション内のインスタンスを探し、インスタンスが見つからない場合はコレクションの最初の要素をデフォルト値として返します。
MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));
更新 (Java 8 リリース後):
私 (アラン) が最初にこの回答を投稿してから数年が経ちましたが、この回答で SO ポイントを獲得しているとはまだ信じられません。いずれにしても、Java 8 で言語にクロージャが導入された今、私の答えはかなり異なり、より単純になります。Java 8 では、個別の静的ユーティリティ クラスは必要ありません。したがって、述語に一致する最初の要素を見つけたい場合。
final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).findFirst();
オプション用の JDK 8 API には、、、、および、およびget()
、およびなどの他の「モナディック」関数を実行する機能があります。isPresent()
orElse(defaultUser)
orElseGet(userSupplier)
orElseThrow(exceptionSupplier)
map
flatMap
filter
述語に一致するすべてのユーザーを単純に収集する場合は、 を使用Collectors
して目的のコレクションでストリームを終了します。
final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
boolean isAuthorized = userService.isAuthorized(u);
return isAuthorized;
}).collect(Collectors.toList());
Java 8 ストリームの仕組みに関するその他の例については、こちらを参照してください。
Apache Commons のCollectionUtils.filter(Collection,Predicate)を使用します。
「最善」の方法は広すぎる要求です。「最短」ですか?「最速」?「読みやすい」?その場でフィルターをかけますか、それとも別のコレクションに入れますか?
最も簡単な (しかし、最も読みにくい) 方法は、反復して Iterator.remove() メソッドを使用することです。
Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
Foo foo = it.next();
if( !condition(foo) ) it.remove();
}
読みやすくするために、ユーティリティ メソッドにラップすることができます。次に、IPredicate インターフェイスを作成し、そのインターフェイスの匿名実装を作成して、次のようにします。
CollectionUtils.filterInPlace(col,
new IPredicate<Foo>(){
public boolean keepIt(Foo foo) {
return foo.isBar();
}
});
ここで、filterInPlace() はコレクションを反復し、Predicate.keepIt() を呼び出して、インスタンスをコレクションに保持するかどうかを学習します。
このタスクのためだけにサードパーティのライブラリを導入する正当な理由がわかりません。
ジェネリックをサポートする最新のコレクション フレームワークについては、Google コレクションを検討してください。
更新: Google コレクション ライブラリは非推奨になりました。代わりにGuavaの最新リリースを使用する必要があります。述語に基づくフィルタリングのメカニズムを含む、コレクション フレームワークに対する拡張機能はすべて同じです。
Java 8 を待ちます。
List<Person> olderThan30 =
//Create a Stream from the personList
personList.stream().
//filter the element to select only those with age >= 30
filter(p -> p.age >= 30).
//put those filtered elements into a new List.
collect(Collectors.toList());
Eclipseコレクションを使用して組み込みのJDKリストとMutableListをフィルタリングする方法を見てみましょう。
List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);
3未満の数値をフィルタリングする場合は、次の出力が期待されます。
List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);
Java8ラムダをとして使用してフィルタリングする方法は次のとおりですPredicate
。
Assert.assertEquals(selected, Iterate.select(jdkList, each -> each < 3));
Assert.assertEquals(rejected, Iterate.reject(jdkList, each -> each < 3));
Assert.assertEquals(selected, ecList.select(each -> each < 3));
Assert.assertEquals(rejected, ecList.reject(each -> each < 3));
匿名の内部クラスをとして使用してフィルタリングする方法は次のとおりですPredicate
。
Predicate<Integer> lessThan3 = new Predicate<Integer>()
{
public boolean accept(Integer each)
{
return each < 3;
}
};
Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3));
Assert.assertEquals(selected, ecList.select(lessThan3));
述語ファクトリを使用してJDKリストとEclipseコレクションMutableListsをフィルタリングする代わりの方法をいくつか示します。
Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));
Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));
これは、述語にオブジェクトを割り当てないバージョンです。代わりに、を使用するメソッドでPredicates2ファクトリを使用します。selectWith
Predicate2
Assert.assertEquals(
selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));
ネガティブな条件でフィルタリングしたい場合があります。Eclipseコレクションには、と呼ばれる特別なメソッドがありreject
ます。
Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));
Assert.assertEquals(rejected, ecList.reject(lessThan3));
このメソッドpartition
は、によって選択され、拒否された要素を含む2つのコレクションを返しPredicate
ます。
PartitionIterable<Integer> jdkPartitioned = Iterate.partition(jdkList, lessThan3);
Assert.assertEquals(selected, jdkPartitioned.getSelected());
Assert.assertEquals(rejected, jdkPartitioned.getRejected());
PartitionList<Integer> ecPartitioned = gscList.partition(lessThan3);
Assert.assertEquals(selected, ecPartitioned.getSelected());
Assert.assertEquals(rejected, ecPartitioned.getRejected());
注:私はEclipseコレクションのコミッターです。
イテレータではなく、コレクション自体をフィルタしてもよろしいですか?
org.apache.commons.collections.iterators.FilterIteratorを参照してください
またはApache Commons org.apache.commons.collections4.iterators.FilterIteratorのバージョン4を使用
セットアップ:
public interface Predicate<T> {
public boolean filter(T t);
}
void filterCollection(Collection<T> col, Predicate<T> predicate) {
for (Iterator i = col.iterator(); i.hasNext();) {
T obj = i.next();
if (predicate.filter(obj)) {
i.remove();
}
}
}
使用法:
List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
public boolean filter(MyObject obj) {
return obj.shouldFilter();
}
});
単純で単純なJavaはどうですか
List<Customer> list ...;
List<Customer> newList = new ArrayList<>();
for (Customer c : list){
if (c.getName().equals("dd")) newList.add(c);
}
シンプルで読みやすく、簡単です (そして Android で動作します!) しかし、Java 8 を使用している場合は、1 行で実行できます。
List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());
toList() は静的にインポートされることに注意してください
GoogleのGuavaライブラリのCollections2.filter(Collection、Predicate)メソッドは、探しているものを実行します。
ForEach DSLを使用すると、次のように記述できます。
import static ch.akuhn.util.query.Query.select;
import static ch.akuhn.util.query.Query.$result;
import ch.akuhn.util.query.Select;
Collection<String> collection = ...
for (Select<String> each : select(collection)) {
each.yield = each.value.length() > 3;
}
Collection<String> result = $result();
[The、quick、brown、fox、jumps、over、the、lazy、dog]のコレクションを考えると、これは[quick、brown、jumps、over、lazy]、つまりすべての文字列が3文字より長いという結果になります。
ForEachDSLでサポートされているすべての反復スタイルは次のとおりです。
AllSatisfy
AnySatisfy
Collect
Counnt
CutPieces
Detect
GroupedBy
IndexOf
InjectInto
Reject
Select
詳細については、https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEachを参照してください。
これは、実際のクロージャーの欠如と相まって、Java に対する私の最大の不満です。正直なところ、上記の方法のほとんどは非常に読みやすく、非常に効率的です。しかし、.Net、Erlang などに時間を費やすと、言語レベルで統合されたリスト内包表記により、すべてが非常にきれいになります。言語レベルでの追加がなければ、Java はこの分野の他の多くの言語ほどきれいにはなりません。
パフォーマンスが非常に重要な場合は、Google のコレクションが適しています (または、独自の単純な述語ユーティリティを作成します)。Lambdaj 構文は一部の人にとっては読みやすいですが、それほど効率的ではありません。
そして、私が書いたライブラリがあります。私はその効率に関する質問を無視します (そうです、それは悪いです) ...... はい、私はそれが明らかにリフレクションに基づいていることを知っており、実際には使用していませんが、動作します:
LinkedList<Person> list = ......
LinkedList<Person> filtered =
Query.from(list).where(Condition.ensure("age", Op.GTE, 21));
また
LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");
を使用するjava 8
と、具体的lambda expression
には、次の例のように簡単に実行できます。
myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())
product
内部myProducts
コレクションごとに、 の場合prod.price>10
、この製品を新しいフィルタリングされたリストに追加します。
コレクションの内容をコピーせずに関数型アルゴリズムを適用することをサポートする、拡張された Iterable クラスを作成しました。
使用法:
List<Integer> myList = new ArrayList<Integer>(){ 1, 2, 3, 4, 5 }
Iterable<Integer> filtered = Iterable.wrap(myList).select(new Predicate1<Integer>()
{
public Boolean call(Integer n) throws FunctionalException
{
return n % 2 == 0;
}
})
for( int n : filtered )
{
System.out.println(n);
}
上記のコードは実際に実行されます
for( int n : myList )
{
if( n % 2 == 0 )
{
System.out.println(n);
}
}
JFilter http://code.google.com/p/jfilter/が要件に最適です。
JFilter は、Java Bean のコレクションをクエリするためのシンプルで高性能なオープン ソース ライブラリです。
主な機能
- コレクション (java.util.Collection、java.util.Map および Array) プロパティのサポート。
- 任意の深さのコレクション内でのコレクションのサポート。
- 内部クエリのサポート。
- パラメータ化されたクエリのサポート。
- 数百ミリ秒で 100 万件のレコードをフィルタリングできます。
- Filter ( query ) はシンプルな json 形式で与えられ、Mangodb クエリのようです。以下にいくつかの例を示します。
- { "id":{"$le":"10"}
- object id プロパティは 10 以下です。
- { "id": {"$in":["0", "100"]}}
- object id プロパティは 0 または 100 です。
- {"lineItems":{"lineAmount":"1"}}
- パラメータ化された型の lineItems コレクション プロパティの lineAmount は 1 です。
- { "$and":[{"id": "0"}, {"billingAddress":{"city":"DEL"}}]}
- ここで、id プロパティは 0 で、billingAddress.city プロパティは DEL です。
- {"lineItems":{"taxes":{ "key":{"code":"GST"}, "value":{"$gt": "1.01"}}}}
- ここで、パラメーター化されたタイプの税金マップ タイプ プロパティを持つパラメーター化されたタイプの lineItems コレクション プロパティは、1.01 より大きい GST 値に等しいコードを持ちます。
- {'$or':[{'code':'10'},{'skus': {'$and':[{'price':{'$in':['20', '40']} }, {'code':'RedApple'}]}}]}
- 製品コードが 10 または SKU 価格が 20 と 40 で、SKU コードが「RedApple」であるすべての製品を選択します。
コレクション クエリ エンジン (CQEngine)を使用します。これは、これを行う最も速い方法です。
シンプルな Java8 以前のソリューション:
ArrayList<Item> filtered = new ArrayList<Item>();
for (Item item : items) if (condition(item)) filtered.add(item);
残念ながら、このソリューションは完全に汎用的ではなく、指定されたコレクションの型ではなくリストを出力します。また、条件が複雑でない限り、ライブラリを導入したり、このコードをラップする関数を作成したりすることは、やり過ぎのように思えますが、条件の関数を作成することはできます。
グアバで:
Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);
Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
@Override
public boolean apply(Integer i) {
return i % 2 == 0;
}
});
System.out.println(collection); // Prints 1, 3, 5