3

各ペアにメソッドがある KeyValuePairs のリストが与えられた場合、一意の値の(または)getValue()を取得する最速の方法は何でしょうか?ListSet

以下のすべてで、許容できる結果が得られます。u1予想されるリストサイズ (約 1000-2000 KVP) よりも速いようです。

より良く(より速く)できるでしょうか?

private static Set<String> u1(List<_KVPair> pairs) {
    Set<String> undefined = new HashSet<String>();

    for (_KVPair pair : pairs) {
        undefined.add(pair.getValue());
    }

    if (undefined.size() == 1) {
        return new HashSet<String>();
    }
    return undefined;
}

private static List<String> u2(List<_KVPair> pairs) {

    List<String> undefined = new ArrayList<String>();
    for (_KVPair pair : pairs) {
        if (!undefined.contains(pair.getValue())) {
            undefined.add(pair.getValue());
        }
    }

    return undefined;
}

private static List<String> u3(List<_KVPair> pairs) {

    List<String> undefined = new LinkedList<String>();

    Iterator<_KVPair> it = pairs.iterator();
    while (it.hasNext()) {
        String value = it.next().getValue();
        if (!undefined.contains(value)) {
            undefined.add(value);
        }
    }
    return undefined;
}

約 3600 ペアで、「u3」が勝ちます。約 1500 ペアで、「u1」が勝ちます

4

6 に答える 6

7

最初のオプションはより高速である必要があります。セットを使用する前にサイズを変更することで、さらに高速化できる可能性があります。通常、少数の重複が予想される場合:

Set<String> undefined = new HashSet<String>(pairs.size(), 1);

サイズ変更を防ぐために、負荷係数に 1 を使用したことに注意してください。

好奇心からテストを実行しました(以下のコード)-結果は(コンパイル後)です:

テスト 1 (注: ウォームアップに数分かかります)

元のリストのサイズ = 3,000、重複なし:
set: 8
arraylist: 668
linkedlist: 1166

テスト 2

元のリストのサイズ = 30,000 - すべての文字列が同一:
set: 25
arraylist: 11
linkelist: 13

そのようなことは理にかなっています:

  • 多くの重複がある場合List#contains、重複がより迅速に検出され、大きなセットを割り当てるコスト + ハッシュ アルゴリズムが不利になるため、かなり高速に実行されます。
  • 重複がないか、ほとんどない場合、セットは大差で勝ちます。
public class TestPerf {

    private static int NUM_RUN;
    private static Random r = new Random(System.currentTimeMillis());
    private static boolean random = false; //toggle to false for no duplicates in original list


    public static void main(String[] args) {

        List<String> list = new ArrayList<>();

        for (int i = 0; i < 30_000; i++) {
            list.add(getRandomString());
        }

        //warm up
        for (int i = 0; i < 10_000; i++) {
            method1(list);
            method2(list);
            method3(list);
        }

        NUM_RUN = 100;
        long sum = 0;
        long start = System.nanoTime();
        for (int i = 0; i < NUM_RUN; i++) {
            sum += method1(list);
        }
        long end = System.nanoTime();
        System.out.println("set: " + (end - start) / 1000000);

        sum = 0;
        start = System.nanoTime();
        for (int i = 0; i < NUM_RUN; i++) {
            sum += method2(list);
        }
        end = System.nanoTime();
        System.out.println("arraylist: " + (end - start) / 1000000);

        sum = 0;
        start = System.nanoTime();
        for (int i = 0; i < NUM_RUN; i++) {
            sum += method3(list);
        }
        end = System.nanoTime();
        System.out.println("linkelist: " + (end - start) / 1000000);

        System.out.println(sum);
    }

    private static int method1(final List<String> list) {
        Set<String> set = new HashSet<>(list.size(), 1);
        for (String s : list) {
            set.add(s);
        }
        return set.size();
    }

    private static int method2(final List<String> list) {
        List<String> undefined = new ArrayList<>();
        for (String s : list) {
            if (!undefined.contains(s)) {
                undefined.add(s);
            }
        }
        return undefined.size();
    }

    private static int method3(final List<String> list) {
        List<String> undefined = new LinkedList<>();

        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String value = it.next();
            if (!undefined.contains(value)) {
                undefined.add(value);
            }
        }
        return undefined.size();
    }

    private static String getRandomString() {
        if (!random) {
            return "skdjhflkjrglajhsdkhkjqwhkdjahkshd";
        }
        int size = r.nextInt(100);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            char c = (char) ('a' + r.nextInt(27));
            sb.append(c);
        }
        System.out.println(sb);
        return sb.toString();
    }
}
于 2012-09-25T14:45:58.683 に答える
2

u1最初の行を次のように変更すると、高速化できます。

Set<String> undefined = new HashSet<String>(pairs.size());

それ以外の場合は、値を追加するときにセットのサイズを内部的に大幅に変更する必要があります。

于 2012-09-25T14:44:48.830 に答える
2

更新:以下の編集を参照

できるときにリストを反復しても意味がありません

return new HashSet<_KVPair>(pairs)

最悪のオプションは u2 と u3 です。この場合、最初のリストの項目を 2 番目のリストに追加しList.contains(item)、ループの反復ごとに呼び出します。この操作アプローチO(n^2)-List.contains(item)アイテムを潜在的にリスト全体と比較する必要があります。リストを反復処理し、リストを反復処理する操作を呼び出す必要があるアルゴリズムは避けてください。

ユニークなアイテムが必要な場合は、Set. この項目を並べ替えたい場合は を使用しTreeSet、それ以外の場合は 99% を使用しますHashSet

編集pair.getValue(): ;のセットを取得したいのを逃しました。ただし、アドバイスは同じです。セットを使用List.contains()し、ループで使用しないでください。

于 2012-09-25T14:38:56.863 に答える
1

別のメソッドSort listを 1 つのループに入れることができます。参照が等しい場合は追加された最後の要素の参照を保持することで重複を排除できます。そうでない場合は、新しいリストに追加しないでください。

Collections.sort(pairs)//O(n log n)

Loop
if(!lastAdded.equals(pairs.get(i)))
 {
   //Add to list 
   //change lastAdded
 }
于 2012-09-25T14:53:08.630 に答える
1

オプション 1 が最も速く、最もクリーンであると断言できます。値がすでにそこに含まれているかどうかをチェックするという点で、ハッシュ セットに勝るものはありません。

前の回答で述べたように、リストベースのソリューションはスケーリングしません

于 2012-09-25T14:44:13.907 に答える
-1

指定された回答のいずれも、最終結果から重複を削除するものではなく、重複を削除するだけです。したがって、文字列が 2 回存在する場合でも、最終結果には存在しますが、1 回だけです。それが必要ない場合は、5 分を無駄にしました...

 public Map<String, String> countOccurences(List<String> source){
       Map<String, Integer> result =   new HashMap<>(source.size());
        int temp =0;
        for (String value : source) {
            if(result.containsKey(value)){
                temp = result.get(value);
                temp++;
                result.put(value, temp);
                temp = 0;
            }
            else {
                result.put(value, 1);
            }
        }
    }
    public List<String> sublistSingles(Map<String, Integer> results){
        List<String> duplicatesRemoved = new ArrayList<>(results.size());
        for(Map.Entry<String, Integer> result:results.entrySet()){
            if(result.getValue().equals(1)){
              duplicatesRemoved.add(result.getKey());
            }
        }
        return duplicatesRemoved;
    }
于 2012-09-25T15:04:59.490 に答える