web-dev-qa-db-ja.com

Java(Criteria / SQL-like)のオブジェクトコレクションをどのようにクエリしますか?

数百のメモリ内オブジェクトのコレクションがあり、このリストをクエリして、クエリなどのSQLまたは基準に一致するオブジェクトを返す必要があるとします。たとえば、Carオブジェクトのリストがあり、1960年代に製造されたすべての車を、車のモデル名で注文されたAZで始まるナンバープレートとともに返却したい場合があります。

私は JoSQL について知っています、誰かがこれを使用したことがありますか、または他の/自家製のソリューションの経験がありますか?

29
stian

実稼働アプリケーションで Apache Commons JXPath を使用しました。これにより、JavaのオブジェクトのグラフにXPath式を適用できます。

12
Eric Weilnau

他の回答で説明されているように、フィルタリングはこれを行う1つの方法です。

ただし、フィルタリングはスケーラブルではありません。表面的には、複雑さはO( n )のように見えます(つまり、コレクション内のオブジェクトの数が増える場合、すでにスケーラブルではありません)が、実際には1つまたはそれ以上テストをクエリに応じて各オブジェクトに適用する必要があります。時間計算量はより正確にO( nt )です。ここで tは、各オブジェクトに適用するテストの数です。

そのため、コレクションにオブジェクトが追加されると、および/またはがクエリ内のテストの数が増えると、パフォーマンスが低下します。

インデックス付けと集合論を使用して、これを行う別の方法があります。

1つのアプローチは、コレクションに格納されているオブジェクト内で indexs fields に構築するであり、後でテストします。クエリ。

Carオブジェクトのコレクションがあり、すべてのCarオブジェクトにフィールドcolorがあるとします。クエリが「SELECT * FROM cars WHERE Car.color = 'blue'」と同等であるとします。 Car.colorにインデックスを作成できます。これは、基本的に次のようになります。

'blue' -> {Car{name=blue_car_1, color='blue'}, Car{name=blue_car_2, color='blue'}}
'red'  -> {Car{name=red_car_1, color='red'}, Car{name=red_car_2, color='red'}}

次に、クエリWHERE Car.color = 'blue'を指定すると、青い車のセットをO( 1 )時間計算量で取得できます。クエリに追加のテストがあった場合は、その候補セット内の各車をテストして、クエリの残りのテストと一致するかどうかを確認できます。候補セットはコレクション全体よりも大幅に小さい可能性が高いため、時間計算量は未満 O( n )(エンジニアリングでは)センス、以下のコメントを参照)。コレクションにオブジェクトが追加されても、パフォーマンスはそれほど低下しません。しかし、これはまだ完璧ではありません、読み続けてください。

別のアプローチは、私が永続的なクエリインデックスと呼ぶものです。説明:従来の反復とフィルタリングでは、コレクションが反復され、すべてのオブジェクトがテストされて、クエリと一致するかどうかが確認されます。したがって、フィルタリングは、コレクションに対してクエリを実行するようなものです。スタンディングクエリインデックスは逆で、コレクションは代わりにクエリに対して実行されますが、コレクションは何度でもクエリできますが、コレクション内のオブジェクトごとに1回だけ実行されます。

永続クエリインデックスは、ある種のインテリジェントコレクションにクエリを登録するようなもので、オブジェクトが追加されたり削除されたりします。コレクションの場合、コレクションは、登録されているすべての永続クエリに対して各オブジェクトを自動的にテストします。オブジェクトが永続クエリに一致する場合、コレクションは、そのクエリに一致するオブジェクトの格納専用のセットにオブジェクトを追加/削除することができます。その後、登録されたクエリのいずれかに一致するオブジェクトは、O( 1 )時間計算量で取得できます。

上記の情報は CQEngine(Collection Query Engine) から取得されます。これは基本的に、SQLのようなクエリを使用してJavaコレクションからオブジェクトを取得するためのNoSQLクエリエンジンであり、コレクションを反復処理するオーバーヘッドはありません。上記のアイデアとその他のアイデアに基づいて構築されています。免責事項:私は著者です。オープンソースであり、MavenCentralにあります。役立つと思われる場合は、この回答に賛成してください!

25
npgall

はい、私はそれが古い投稿であることを知っていますが、テクノロジーは毎日登場し、答えは時間とともに変化します。

これはLambdaJで解決するのに良い問題だと思います。あなたはここでそれを見つけることができます: http://code.google.com/p/lambdaj/

ここに例があります:

アクティブな顧客を探す//(反復可能なバージョン)

List<Customer> activeCustomers = new ArrayList<Customer>();  
for (Customer customer : customers) {  
  if (customer.isActive()) {  
    activeCusomers.add(customer);  
  }  
}  

LambdaJバージョン

List<Customer> activeCustomers = select(customers, 
                                        having(on(Customer.class).isActive()));  

もちろん、このような美しさはパフォーマンスに影響を与えます(少し...平均2回)が、もっと読みやすいコードを見つけることができますか?

多くの機能があります。別の例として、並べ替えがあります。

反復の並べ替え

List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
        public int compare(Person p1, Person p2) {
           return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
        }
}); 

ラムダでソート

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 
5
Federico Piazza

Comparatorテーマを続けて、 Googleコレクション APIも確認することをお勧めします。特に、 Predicate と呼ばれるインターフェースがあります。これは、Comparatorと同様の役割を果たし、 Sets.filter のようなフィルタリングメソッドで使用されます。それらには、AND、ORなどを実行するための複合述語実装が多数含まれています。

データセットのサイズによっては、SQLや外部リレーショナルデータベースのアプローチよりも、このアプローチを使用する方が理にかなっている場合があります。

3
joev

単一の具象一致が必要な場合は、クラスにComparatorを実装させてから、すべてのハッシュフィールドが含まれるスタンドアロンオブジェクトを作成し、それを使用して一致のインデックスを返すことができます。コレクション内で複数の(潜在的に)オブジェクトを見つけたい場合は、JoSQLのようなライブラリを使用する必要があります(これは、私が使用した些細なケースでうまく機能しました)。

一般に、私は小さなアプリケーションにもDerbyを埋め込み、Hibernateアノテーションを使用してモデルクラスを定義し、Hibernateにキャッシュスキームを処理させてすべてを高速に保つ傾向があります。

2
Steve Moyer

入力パラメータとして、年の範囲とナンバープレートのパターンを使用するコンパレータを使用します。次に、コレクションを繰り返し処理して、一致するオブジェクトをコピーします。このアプローチでは、カスタムコンパレータのパッケージ全体を作成することになります。

1
Bill the Lizard

Comparatorオプションは、特に匿名クラスを使用する場合(プロジェクトで冗長クラスを作成しないようにするため)は悪くありませんが、最終的に比較のフローを見ると、ループをループするのとほとんど同じです。コレクション全体を自分で、一致するアイテムの条件を正確に指定します。

if (Car car : cars) {
    if (1959 < car.getYear() && 1970 > car.getYear() &&
            car.getLicense().startsWith("AZ")) {
        result.add(car);
    }
}

次に、並べ替えがあります...これは裏側で苦痛かもしれませんが、幸いなことに、クラスCollectionsとそのsortメソッドがあり、そのうちの1つはComparator ..を受け取ります。

0
Yuval