猫のように、年齢、好きな猫の餌など、多くの特性を持つ複雑なオブジェクトがあります。
たくさんの猫がJavaコレクションに格納されており、3歳の猫、またはお気に入りのキャットフードがウィスカである猫をすべて見つける必要があります。確かに、カスタムメソッドを書くことができます特定のプロパティを持つ猫を見つけますが、これは多くのプロパティで面倒になります。これを行う一般的な方法はありますか?
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;
}
もちろん、他の人が言っているように、これはリレーショナルデータベースが何のために作られたかです...
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
インターフェースの実装を作成できます。
Java 8ラムダ式を使用すると、次のようなことができます
cats.stream()
.filter( c -> c.getAge() == 3 && c.getFavoriteFood() == WHISKAS )
.collect(Collectors.toList());
概念的にはGuava Predicateアプローチと同じですが、ラムダを使用するとずっときれいに見えます
おそらくOPの有効な回答ではありませんが、同様のニーズを持つ人々には注意する価値があります。 :)
Jxpath を使用することをお勧めします。xpathのようなオブジェクトグラフでクエリを実行できます。
JXPathContext.newContext(cats).
getValue("//*[@drinks='milk']")
再び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))
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です。
参考までに、グアバを使用するこの質問には他に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;
}
});
コモンズコレクションの使用:
EqualPredicate nameEqlPredicate = new EqualPredicate(3);
BeanPredicate beanPredicate = new BeanPredicate("age", nameEqlPredicate);
return CollectionUtils.filter(cats, beanPredicate);
述語、フィルター、およびカスタムイテレーター/コンパレーターに基づくソリューションは素晴らしいですが、データベースインデックスの類似物を提供しません。たとえば、猫のコレクションをさまざまな方法で検索したいと思います。性別、年齢、および年齢で、1)[性別、年齢] 2)[年齢]という2つのインデックスのように見えます。そのインデックスによってアクセスされる値は、コレクション全体を反復することなく高速ルックアップを実装するためにハッシュできます。そのような解決策はどこにありますか?
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;
}
});
この方法を使用すると思います。
.NETでLINQ forを使用するものによく似ています
Javaの "本物の" LINQ実装はまだありませんが、 Quaere を見ると、説明したことを十分に簡単に実行できます。
JoSQLのようなものを使用して、コレクションに対して「SQL」を記述できます。 http://josql.sourceforge.net/
あなたが望むもののように聞こえますが、より複雑なクエリを実行できるという利点があります。
Apache Commonsプロジェクトの汎用コードをいくつか試すことができます。 Collections サブプロジェクトは、特定のPredicateに一致するオブジェクトを検索するためのコードと、多数の述語(equals、null、instanceofなど)を提供します。 BeanUtils サブプロジェクトを使用すると、Beanのプロパティをテストする述語を作成できます。
CollectionUtils クラスを使用して、コレクション内を検索します。これにはいくつかのメソッドがありますが、特にselect()メソッドをチェックしてください。次のクラスを使用して述語を構築するか、独自の記述を作成します。 Predicate 、 PredicateUtils 、 BeanPredicate 。
これは時々少し面倒ですが、少なくとも一般的です! :-)
JFilter http://code.google.com/p/jfilter/ は要件に合っています。
JFilterは、Java Beansのコレクションを照会するためのシンプルで高性能なオープンソースライブラリです。
主な機能
次の機能としてリストからアイテムを検索できます。幸運を!
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;
}
これらのオブジェクトをデータベースに保存できます。本格的なデータベースサーバーのオーバーヘッドが必要ない場合は、HSQLDBなどの埋め込みサーバーを使用できます。次に、HibernateまたはBeanKeeper(使いやすい)または他のORMを使用して、オブジェクトをテーブルにマッピングできます。 OOモデルを使用し続けると、データベースから高度なストレージとクエリの利点が得られます。
グアバには、このようなタイプの問題に関して非常に強力な検索機能があります。たとえば、プロパティの1つに基づいてオブジェクトを検索するエリアの場合、以下を検討できます。
Iterables.tryFind(listOfCats, new Predicate<Cat>(){
@Override
boolean apply(@Nullable Cat input) {
return "tom".equalsIgnoreCase(input.name());
}
}).or(new Cat("Tom"));
トム猫がlistOfCatsに含まれていない可能性がある場合、それが返されるため、NPEを回避できます。
探したい特定の値でパラメーター化できるサーチャークラスのアイデアを次に示します。
さらに進んで、おそらく目的の値を持つマップにプロパティの名前を保存することもできます。この場合、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.
}
}
非常に一般的な問題であり、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;
}
}
お役に立てれば