数百のメモリ内オブジェクトのコレクションがあり、このリストをクエリして、クエリのような SQL または Criteria に一致するオブジェクトを返す必要があるとします。たとえば、自動車オブジェクトのリストがあり、1960 年代に製造され、ナンバー プレートが AZ で始まるすべての自動車を、自動車モデルの名前順に並べて返したいとします。
私はJoSQLについて知っていますか、誰かがこれを使用したことがありますか、または他の/自家製のソリューションの経験はありますか?
数百のメモリ内オブジェクトのコレクションがあり、このリストをクエリして、クエリのような SQL または Criteria に一致するオブジェクトを返す必要があるとします。たとえば、自動車オブジェクトのリストがあり、1960 年代に製造され、ナンバー プレートが AZ で始まるすべての自動車を、自動車モデルの名前順に並べて返したいとします。
私はJoSQLについて知っていますか、誰かがこれを使用したことがありますか、または他の/自家製のソリューションの経験はありますか?
他の回答で説明されているように、フィルタリングはこれを行う1つの方法です。
ただし、フィルタリングはスケーラブルではありません。表面的には、複雑さはO( n )のように見えます(つまり、コレクション内のオブジェクトの数が増える場合、すでにスケーラブルではありません)が、実際には、クエリに応じて1つ以上のテストを各オブジェクトに適用する必要があるため、時間より正確な複雑さはO(nt)です。ここで、tは各オブジェクトに適用するテストの数です。
そのため、コレクションにオブジェクトが追加されたり、クエリ内のテストの数が増えたりすると、パフォーマンスが低下します。
インデックス付けと集合論を使用して、これを行う別の方法があります。
1つのアプローチは、コレクションに格納されているオブジェクト内のフィールドにインデックスを作成し、後でクエリでテストすることです。
オブジェクトのコレクションが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'}}
次に、クエリを指定すると、青い車のセットをO( 1)時間計算量WHERE Car.color = 'blue'
で取得できます。クエリに追加のテストがあった場合は、その候補セット内の各車をテストして、クエリ内の残りのテストと一致するかどうかを確認できます。候補セットはコレクション全体よりも大幅に小さい可能性が高いため、時間計算量はO(n )未満です(エンジニアリングの意味では、以下のコメントを参照してください)。コレクションにオブジェクトを追加しても、パフォーマンスはそれほど低下しません。しかし、これはまだ完璧ではありません、読み続けてください。
もう1つのアプローチは、私がスタンディングクエリインデックスと呼ぶものです。説明:従来の反復とフィルタリングでは、コレクションが反復され、すべてのオブジェクトがテストされて、クエリと一致するかどうかが確認されます。したがって、フィルタリングは、コレクションに対してクエリを実行するようなものです。永続的なクエリインデックスは逆で、コレクションは代わりにクエリに対して実行されますが、コレクションは何度でもクエリされる可能性がありますが、コレクション内のオブジェクトごとに1回だけ実行されます。
スタンディングクエリインデックスは、ある種のインテリジェントコレクションにクエリを登録するようなものです。たとえば、オブジェクトがコレクションに追加されたりコレクションから削除されたりすると、コレクションは、登録されているすべてのスタンディングクエリに対して各オブジェクトを自動的にテストします。オブジェクトが永続クエリに一致する場合、コレクションは、そのクエリに一致するオブジェクトの格納専用のセットにオブジェクトを追加/削除することができます。その後、登録されたクエリのいずれかに一致するオブジェクトは、O(1)時間計算量で取得できます。
上記の情報は、CQEngine(コレクションクエリエンジン)から取得されます。これは基本的に、コレクションを反復処理するオーバーヘッドなしに、SQLのようなクエリを使用してJavaコレクションからオブジェクトを取得するためのNoSQLクエリエンジンです。これは、上記のアイデアに加えて、さらにいくつかのアイデアに基づいて構築されています。免責事項:私は著者です。オープンソースであり、MavenCentralにあります。役に立ったら、この回答に賛成してください。
私は実稼働アプリケーションでApache Commons JXPathを使用しました。Java のオブジェクトのグラフに XPath 式を適用できます。
はい、私はそれが古い投稿であることを知っていますが、テクノロジーは毎日登場し、答えは時間とともに変化します.
これは 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());
更新: Java 8 以降では、次のようなすぐに使用できるラムダ式を使用できます。
List<Customer> activeCustomers = customers.stream()
.filter(Customer::isActive)
.collect(Collectors.toList());
テーマを続けて、 Google Collections APIComparator
も見てみたいと思うかもしれません。特に、 Predicateと呼ばれるインターフェースがあります。これは、 Sets.filterのようなフィルタリング メソッドで使用できる単純なインターフェースであるという点で、と同様の役割を果たします。それらには、AND、OR などを実行するための複合述語の実装が多数含まれています。Comparator
データ セットのサイズによっては、SQL または外部リレーショナル データベース アプローチよりも、このアプローチを使用する方が理にかなっている場合があります。
単一の具体的な一致が必要な場合は、クラスに Comparator を実装させてから、ハッシュされたすべてのフィールドを含むスタンドアロン オブジェクトを作成し、それを使用して一致のインデックスを返すことができます。コレクション内で複数の (可能性のある) オブジェクトを見つけたい場合は、JoSQL のようなライブラリを使用する必要があります (これは、私が使用した些細なケースではうまく機能しました)。
一般に、私は小さなアプリケーションにも Derby を組み込み、Hibernate アノテーションを使用してモデル クラスを定義し、Hibernate にキャッシング スキームを処理させてすべてを高速に保つ傾向があります。
入力パラメーターとして年式とナンバー プレート パターンの範囲を取る Comparator を使用します。次に、コレクションを繰り返し処理し、一致するオブジェクトをコピーします。このアプローチでは、カスタム Comparators のパッケージ全体を作成することになる可能性があります。
特に匿名クラスを使用する場合 (プロジェクトで冗長なクラスを作成しないようにするため)、このComparator
オプションは悪くありませんが、最終的に比較の流れを見ると、コレクション全体を自分でループして、正確に指定するのとほとんど同じです。一致するアイテムの条件:
if (Car car : cars) {
if (1959 < car.getYear() && 1970 > car.getYear() &&
car.getLicense().startsWith("AZ")) {
result.add(car);
}
}
次に、並べ替えがあります...これは面倒かもしれませんが、幸いなことに、クラスCollections
とそのメソッドがあり、そのうちの1つが...sort
を受け取ります。Comparator