web-dev-qa-db-ja.com

HashSetとArrayListにはパフォーマンスが含まれます

大量のデータを処理するとき、私はしばしば次のことをしていることに気づきます。

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つの可能性のいずれかが明らかに効率的ですか?

28
Jorge

セットはパフォーマンスが大幅に向上し(リストのO(n) vs O(n^2))、セットメンバーシップ(contains操作)は非常に目的のセット。

リストのO(1)と比較してHashSetO(n)であるため、containsを頻繁に実行する必要がある場合はリストを使用しないでください。

59
Dici

ArrayListは、データを格納するために配列を使用します。 _ArrayList.contains_の複雑さは、O(n)の複雑さです。したがって、基本的に配列内を何度も検索すると、O(n^2)の複雑さになります。

HashSetは、ハッシュメカニズムを使用して要素をそれぞれのバケットに格納します。 HashSetの操作は、値のリストが長いほど高速になります。 O(1)の要素に到達します。

10
YoungHobbit

テストを行ったので、結果を確認してください:

HashSet、TreeSet、ArrayList、およびLinkedListの同じ文字列アイテムの場合、次の結果があります。

  1. 50.000 UUID
    • 検索項目:e608c7d5-c861-4603-9134-8c636a05a42b(インデックス25.000)
    • hashSet.contains(item)? TRUE 0ミリ秒
    • treeSet.contains(item)? TRUE 0ミリ秒
    • arrayList.contains(item)? TRUE 2ミリ秒
    • linkedList.contains(item)? TRUE 3ミリ秒
  2. 5.000.000 UUID
    • 検索項目:61fb2592-3186-4256-a084-6c96f9322a86(インデックス25.000)
    • hashSet.contains(item)? TRUE 0ミリ秒
    • treeSet.contains(item)? TRUE 0ミリ秒
    • arrayList.contains(item)? TRUE 1ミリ秒
    • linkedList.contains(item)? TRUE 2ミリ秒
  3. 5.000.000 UUID
    • 検索項目:db568900-c874-46ba-9b44-0e1916420120(インデックス2.500.000)
    • hashSet.contains(item)? TRUE 0ミリ秒
    • treeSet.contains(item)? TRUE 0ミリ秒
    • arrayList.contains(item)? TRUE 33ミリ秒
    • linkedList.contains(item)? TRUE 65ミリ秒

上記の結果に基づいて、配列リストとセットを使用しても大きな違いはありません。おそらく、このコードを変更してStringObjectに置き換えて、違いを確認してみてください...

    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");
    }
7
urs86ro

リストが必要ない場合は、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)です。

5
Peter Lawrey

リスト自体に要素を追加できます。次に、重複除去に-

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()を使用して、一意の値のみを持つようにすることもできます。

0