web-dev-qa-db-ja.com

コレクション内で特定のプロパティを持つすべてのオブジェクトを検索する

猫のように、年齢、好きな猫の餌など、多くの特性を持つ複雑なオブジェクトがあります。

たくさんの猫がJavaコレクションに格納されており、3歳の猫、またはお気に入りのキャットフードがウィスカである猫をすべて見つける必要があります。確かに、カスタムメソッドを書くことができます特定のプロパティを持つ猫を見つけますが、これは多くのプロパティで面倒になります。これを行う一般的な方法はありますか?

85
Jake

check(Cat)メソッドを定義するインターフェイスのインスタンスを取るメソッドを作成できます。このメソッドは、必要なプロパティチェックを使用して実装できます。

いっそのこと、それを汎用にする:

public interface Checker<T> {
    public boolean check(T obj);
}

public class CatChecker implements Checker<Cat> {
    public boolean check(Cat cat) {
        return (cat.age == 3); // or whatever, implement your comparison here
    }
}

// put this in some class
public static <T> Collection<T> findAll(Collection<T> coll, Checker<T> chk) {
    LinkedList<T> l = new LinkedList<T>();
    for (T obj : coll) {
         if (chk.check(obj))
             l.add(obj);
    }
    return l;
}

もちろん、他の人が言っているように、これはリレーショナルデータベースが何のために作られたかです...

45
David Z

Commons collections APIを試してください:

List<Cat> bigList = ....; // master list

Collection<Cat> smallList = CollectionUtils.select(bigList, new Predicate() {
    public boolean evaluate(Object o) {
        Cat c = (Cat)o;
        return c.getFavoriteFood().equals("Wiskas") 
            && c.getWhateverElse().equals(Something);
    }
});

もちろん、匿名クラスを使用する必要はありませんevery time、よく使用される検索用にPredicateインターフェースの実装を作成できます。

62
Brian Dilley

この種の問題には、Googleコレクション(現在は Guava )を使用しています。 Iterablesというクラスがあり、Predicateというインターフェイスをメソッドのパラメーターとして実際に使用できます。

Cat theOne = Iterables.find(cats, new Predicate<Cat>() {
    public boolean apply(Cat arg) { return arg.age() == 3; }
});

それを確認してください こちら

55
rcouto

Java 8ラムダ式を使用すると、次のようなことができます

cats.stream()
    .filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
    .collect(Collectors.toList());

概念的にはGuava Predicateアプローチと同じですが、ラムダを使用するとずっときれいに見えます

おそらくOPの有効な回答ではありませんが、同様のニーズを持つ人々には注意する価値があります。 :)

53
Adrian Shum

Jxpath を使用することをお勧めします。xpathのようなオブジェクトグラフでクエリを実行できます。

JXPathContext.newContext(cats).
     getValue("//*[@drinks='milk']")
11
flybywire

再びcommonsコレクションAPIを使用する場合:述部を個別に実装すると、「チェッカー」タイプのコードを取得します。

public class CatPredicate implements Predicate {

    private int age; 


    public CatPredicate(int age) {
        super();
        this.age = age;
    }


    @Override
    public boolean evaluate(Object o) {
        Cat c (Cat)o;
        return c.getAge()==this.age;
    }

}

として使用されます:-

CollectionUtils.filter(catCollectionToFilter, new CatPredicate(3))
5
user151888

lambdaj を使用できます。これらのようなことは簡単で、構文は本当にスムーズです:

Person me = new Person("Mario", "Fusco", 35);
Person luca = new Person("Luca", "Marrocco", 29);
Person biagio = new Person("Biagio", "Beatrice", 39);
Person celestino = new Person("Celestino", "Bellone", 29);
List<Person> meAndMyFriends = asList(me, luca, biagio, celestino);
List<Person> oldFriends = filter(having(on(Person.class).getAge(), greaterThan(30)), meAndMyFriends);

そして、あなたはもっと複雑なことをすることができます。マッチャーに hamcrest を使用します。これはJavaスタイルではないと主張する人もいますが、この男がJavaを使って少しの関数型プログラミングを作成したのは楽しいです。ソースコードもご覧ください。かなりSFです。

4
Gismo Ranas

参考までに、グアバを使用するこの質問には他に3つの回答がありますが、質問に回答するものはありません。質問者は、一致するプロパティを持つすべての猫を見つけたいと言っています。 Iterables.findは、存在する場合は1つだけに一致します。 Guavaを使用している場合は、Iterables.filterを使用してこれを実現する必要があります。例:

Iterable<Cat> matches = Iterables.filter(cats, new Predicate<Cat>() {
    @Override
    public boolean apply(Cat input) {
        return input.getAge() == 3;
    }
});
3
Breeno

コモンズコレクションの使用:

EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);

述語、フィルター、およびカスタムイテレーター/コンパレーターに基づくソリューションは素晴らしいですが、データベースインデックスの類似物を提供しません。たとえば、猫のコレクションをさまざまな方法で検索したいと思います。性別、年齢、および年齢で、1)[性別、年齢] 2)[年齢]という2つのインデックスのように見えます。そのインデックスによってアクセスされる値は、コレクション全体を反復することなく高速ルックアップを実装するためにハッシュできます。そのような解決策はどこにありますか?

1

Google Guavaを使用します。

final int lastSeq = myCollections.size();
Clazz target = Iterables.find(myCollections, new Predicate<Clazz>() {
    @Override
    public boolean apply(@Nullable Clazz input) {
      return input.getSeq() == lastSeq;
    }
});

この方法を使用すると思います。

1
Honeymon

.NETでLINQ forを使用するものによく似ています

Javaの "本物の" LINQ実装はまだありませんが、 Quaere を見ると、説明したことを十分に簡単に実行できます。

1
Eric Petroelje

JoSQLのようなものを使用して、コレクションに対して「SQL」を記述できます。 http://josql.sourceforge.net/

あなたが望むもののように聞こえますが、より複雑なクエリを実行できるという利点があります。

1
duwanis

Apache Commonsプロジェクトの汎用コードをいくつか試すことができます。 Collections サブプロジェクトは、特定のPredicateに一致するオブジェクトを検索するためのコードと、多数の述語(equals、null、instanceofなど)を提供します。 BeanUtils サブプロジェクトを使用すると、Beanのプロパティをテストする述語を作成できます。

CollectionUtils クラスを使用して、コレクション内を検索します。これにはいくつかのメソッドがありますが、特にselect()メソッドをチェックしてください。次のクラスを使用して述語を構築するか、独自の記述を作成します。 PredicatePredicateUtilsBeanPredicate

これは時々少し面倒ですが、少なくとも一般的です! :-)

1
Rich Dougherty

JFilter http://code.google.com/p/jfilter/ は要件に合っています。

JFilterは、Java Beansのコレクションを照会するためのシンプルで高性能なオープンソースライブラリです。

主な機能

  • コレクション(Java.util.Collection、Java.util.MapおよびArray)プロパティのサポート。
  • 任意の深さのコレクション内のコレクションのサポート。
  • 内部クエリのサポート。
  • パラメータ化されたクエリのサポート。
  • わずか100ミリ秒で100万件のレコードをフィルタリングできます。
  • フィルター(クエリ)は、シンプルなjson形式で提供され、Mangodbクエリのようなものです。次に例を示します。
    • {"id":{"$ le": "10"}
      • ここで、オブジェクトIDプロパティは10以下です。
    • {"id":{"$ in":["0"、 "100"]}}
      • ここで、オブジェクトIDプロパティは0または100です。
    • {"lineItems":{"lineAmount": "1"}}
      • パラメータ化されたタイプのlineItemsコレクションプロパティのlineAmountは1です。
    • {"$ and":[{"id": "0"}、{"billingAddress":{"city": "DEL"}}]}
      • idプロパティは0で、billingAddress.cityプロパティはDELです。
    • {"lineItems":{"taxes":{"キー":{"コード": "GST"}、 "値":{"$ gt": "1.01"}}}} {.____。]
      • ここで、税金を含むパラメータ化されたタイプのlineItemsコレクションプロパティは、パラメータ化されたタイプのマップタイププロパティに、1.01より大きいGST値に等しいコードがあります。
    • {'$ or':[{'code': '10'}、{'skus':{'$ and':[{'price':{'$ in':['20'、 '40']} }、{'code': 'RedApple'}]}}]}
      • 製品コードが10または20および40のsku価格で、skuコードが「RedApple」であるすべての製品を選択します。
0
Kamran Ali Khan

次の機能としてリストからアイテムを検索できます。幸運を!

int _searchList(List<T> list,T item) {
int idx = -1;
idx = Collections.binarySearch(list,item,
        new Comparator<T>() {
            public int compare(Titm1,
                    Titm2) {

                // key1
                if (itm1.key1.compareTo(itm2.key1) != 0) {
                    return itm1.key2.compareTo(itm2.key2);
                }

                // key2
                return itm1.key2
                        .compareTo(itm2.key2);
            }
        });

return idx;

}

0
han

これらのオブジェクトをデータベースに保存できます。本格的なデータベースサーバーのオーバーヘッドが必要ない場合は、HSQLDBなどの埋め込みサーバーを使用できます。次に、HibernateまたはBeanKeeper(使いやすい)または他のORMを使用して、オブジェクトをテーブルにマッピングできます。 OOモデルを使用し続けると、データベースから高度なストレージとクエリの利点が得られます。

0
Cesar

グアバには、このようなタイプの問題に関して非常に強力な検索機能があります。たとえば、プロパティの1つに基づいてオブジェクトを検索するエリアの場合、以下を検討できます。

Iterables.tryFind(listOfCats, new Predicate<Cat>(){
    @Override
    boolean apply(@Nullable Cat input) {
        return "tom".equalsIgnoreCase(input.name());
    }
}).or(new Cat("Tom"));

トム猫がlistOfCatsに含まれていない可能性がある場合、それが返されるため、NPEを回避できます。

0
Ivan Hristov

探したい特定の値でパラメーター化できるサーチャークラスのアイデアを次に示します。

さらに進んで、おそらく目的の値を持つマップにプロパティの名前を保存することもできます。この場合、Catクラスのリフレクションを使用して、プロパティ名を指定して適切なメソッドを呼び出します。

public class CatSearcher {

  private Integer ageToFind = null;
  private String  foodToFind = null;

  public CatSearcher( Integer age, String food ) {
    this.ageToFind = age;
    this.foodToFind = food;
  }

  private boolean isMatch(Cat cat) {
    if ( this.ageToFind != null && !cat.getAge().equals(this.ageToFind) ) {
       return false;
    }
    if ( this.foodToFind != null && !cat.getFood().equals(this.foodToFind) {
       return false;
    }
    return true;
  }

  public Collection<Cat> search( Collection<Cat> listToSearch ) {
    // details left to the imagination, but basically iterate over
    // the input list, call isMatch on each element, and if true
    // add it to a local collection which is returned at the end.
  }

}
0
Dave Costa

非常に一般的な問題であり、Googleコレクションを使用しましたが、ここに私のコードがあります

public class FindByIdPredicate implements Predicate<IDObject> {

private Long entityId;

public FindByIdPredicate(final Long entityId) {
    this.entityId = entityId;

}

@Override
public boolean apply(final IDObject input) {
    return input.getId().equals(this.entityId);
}

/**
     * Pass in the Collection
 * @param Collection
 * @return IdObject if present or null
 */
public IDObject getEntity(final Collection<? extends IDObject> collection) {

    for (IDObject idObject : collection) {
        if (this.apply(idObject)) {
            return idObject;
        }
    }
    return null;

}

/**
 * @param Set
 * @return IdObject if present or null
 */
@SuppressWarnings("unchecked")
public <T> T getEntity(final Set<? extends IDObject> set) {

    for (IDObject idObject : set) {
        if (this.apply(idObject)) {
            return (T) idObject;
        }
    }
    return null;

}

}

お役に立てれば

0
vsingh