大量のデータを処理するとき、私はしばしば次のことをしていることに気づきます。
HashSet<String> set = new HashSet<String> ();
//Adding elements to the set
ArrayList<String> list = new ArrayList<String> (set);
リスト内のセットの内容を「ダンプ」するようなもの。追加する要素には削除したい重複が含まれていることが多いので、通常これを行います。これは、それらを削除する簡単な方法のようです。
その目的のみを念頭に置いて(重複を避ける)、次のように書くこともできます。
ArrayList<String> list = new ArrayList<String> ();
// Processing here
if (! list.contains(element)) list.add(element);
//More processing here
したがって、セットをリストに「ダンプ」する必要はありません。ただし、各要素を挿入する前に小さなチェックを行うことになります(HashSetも同様だと想定しています)。
2つの可能性のいずれかが明らかに効率的ですか?
セットはパフォーマンスが大幅に向上し(リストのO(n)
vs O(n^2)
)、セットメンバーシップ(contains
操作)は非常に目的のセット。
リストのO(1)
と比較してHashSet
はO(n)
であるため、contains
を頻繁に実行する必要がある場合はリストを使用しないでください。
ArrayList
は、データを格納するために配列を使用します。 _ArrayList.contains
_の複雑さは、O(n)の複雑さです。したがって、基本的に配列内を何度も検索すると、O(n^2)
の複雑さになります。
HashSet
は、ハッシュメカニズムを使用して要素をそれぞれのバケットに格納します。 HashSet
の操作は、値のリストが長いほど高速になります。 O(1)
の要素に到達します。
テストを行ったので、結果を確認してください:
HashSet、TreeSet、ArrayList、およびLinkedListの同じ文字列アイテムの場合、次の結果があります。
上記の結果に基づいて、配列リストとセットを使用しても大きな違いはありません。おそらく、このコードを変更してStringをObjectに置き換えて、違いを確認してみてください...
public static void main(String[] args) {
Set<String> hashSet = new HashSet<>();
Set<String> treeSet = new TreeSet<>();
List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
List<String> base = new ArrayList<>();
for(int i = 0; i<5000000; i++){
if(i%100000==0) System.out.print(".");
base.add(UUID.randomUUID().toString());
}
System.out.println("\nBase size : " + base.size());
String item = base.get(25000);
System.out.println("SEARCHED ITEM : " + item);
hashSet.addAll(base);
treeSet.addAll(base);
arrayList.addAll(base);
linkedList.addAll(base);
long ms = System.currentTimeMillis();
System.out.println("hashSet.contains(item) ? " + (hashSet.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
System.out.println("treeSet.contains(item) ? " + (treeSet.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
System.out.println("arrayList.contains(item) ? " + (arrayList.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
System.out.println("linkedList.contains(item) ? " + (linkedList.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
}
リストが必要ない場合は、Setを使用します。これは、順序が重要でなく、重複を無視する場合に使用する自然なコレクションです。
重複のないリストが必要な場合、両方を行うことができます。
private Set<String> set = new HashSet<>();
private List<String> list = new ArrayList<>();
public void add(String str) {
if (set.add(str))
list.add(str);
}
この方法では、リストには一意の値のみが含まれ、元の挿入順序は保持され、操作はO(1)です。
リスト自体に要素を追加できます。次に、重複除去に-
HashSet<String> hs = new HashSet<>(); // new hashset
hs.addAll(list); // add all list elements to hashset (this is the dedup, since addAll works as a union, thus removing all duplicates)
list.clear(); // clear the list
list.addAll(hs); // add all hashset elements to the list
Dedupのセットだけが必要な場合は、異なるセットでaddAll()を使用して、一意の値のみを持つようにすることもできます。