Javaの2つの非スパースセットの交差のサイズを見つける最も効率的な方法は何ですか?これは、大きなセットで非常に多くの回数を呼び出す操作なので、最適化が重要です。元のセットを変更できません。
かなり遅いように見えるApache Commons CollectionUtils.intersectionを見てきました。私の現在のアプローチは、2つのセットのうち小さい方を取得して複製し、2つのセットのうち大きい方で.retainAllを呼び出すことです。
public static int getIntersection(Set<Long> set1, Set<Long> set2) {
boolean set1IsLarger = set1.size() > set2.size();
Set<Long> cloneSet = new HashSet<Long>(set1IsLarger ? set2 : set1);
cloneSet.retainAll(set1IsLarger ? set1 : set2);
return cloneSet.size();
}
Google Guava の Sets#intersection(Set, Set)
メソッドを使用するだけです。
SetメソッドretainAll()を使用すると、すべての手動作業を回避できます。
ドキュメントから:
s1.retainAll(s2)— s1をs1とs2の共通部分に変換します。 (2つのセットの共通部分は、両方のセットに共通の要素のみを含むセットです。)
セットのメンバーを比較的狭い範囲の整数に簡単にマッピングできますか?その場合は、BitSetの使用を検討してください。交差点はビット単位で、一度に32人の潜在的なメンバーになります。
両方のイテレータを実行するTreeSet
のように、両方のセットをソートできる場合、共有オブジェクトの数をカウントするためのより高速な方法になる可能性があります。
この操作を頻繁に行う場合、セットをラップしてdirty
フラグを保持して交差操作の結果をキャッシュし、キャッシュされた結果の有効性を追跡し、必要に応じて再計算できると、多くのことが生じる可能性があります。
Java 8ストリームを使用:
set1.stream().filter(s -> set2.contains(s)).collect(Collectors.toList());
セットにある要素の数を数えるためだけに交差を計算する場合は、セットを構築してsize()
を呼び出す代わりに、交差を直接数える必要があることをお勧めします。
カウントのための私の機能:
/**
* Computes the size of intersection of two sets
* @param small first set. preferably smaller than the second argument
* @param large second set;
* @param <T> the type
* @return size of intersection of sets
*/
public <T> int countIntersection(Set<T> small, Set<T> large){
//assuming first argument to be smaller than the later;
//however double checking to be sure
if (small.size() > large.size()) {
//swap the references;
Set<T> tmp = small;
small = large;
large = tmp;
}
int result = 0;
for (T item : small) {
if (large.contains(item)){
//item found in both the sets
result++;
}
}
return result;
}
それは良いアプローチです。現在のソリューションからO(n)パフォーマンスを得ているはずです。
参考までに、集合のコレクションがすべて同じ比較関係を使用してソートされている場合、時間の交点N * Mを反復できます。Nはsmallestセット。Mはセットの数です。
読者に演習として残された実装。 ここに例があります 。
Streams/reduceの交差カウント(呼び出し前にどちらのセットが大きいかを把握していることを前提としています):
public int countIntersect(Set<Integer> largerSet, Set<Integer> smallerSet){
return smallerSet.stream().reduce(0, (a,b) -> largerSet.contains(b)?a+1:a);
}
しかし、他のどこかで、Javaコードは、Javaコードの代わりにネイティブコードとして実装されているため、コードはset操作のSetメソッドよりも高速になることがあります。したがって、BitSetを試して結果をより速くするという提案をバックアップします。