重複して配列のリスト中の各個別のアレイの発生を数えます

バティストMerliot:

問題

私は、配列のリストを持っていると私は重複の発生をカウントします。

例えば、私はこれを持っている場合:

{{1,2,3},
 {1,0,3},
 {1,2,3},
 {5,2,6},
 {5,2,6},
 {5,2,6}}

私はこのようなマップ(または関連するすべてのコレクションを)欲しいです:

{ {1,2,3} -> 2,
  {1,0,3} -> 1,
  {5,2,6} -> 3 }

私も(ここでは例えば、2、1、3)私が唯一の枢機卿に興味があり、配列の値を失うことができます。

私の解決策

私は、次のアルゴリズムを使用します。

  • まずハッシュの配列、および各ハッシュがであるかどうかを確認HashMap<Integer, ArrayList<int[]>>、それは、LETの名前distinctHashキーがハッシュされ、値がそれArrayListに、レットの名前で、rowList、(回避の衝突に)このハッシュのために異なる配列を含みます。

  • ハッシュはしていない場合はdistinctHash、別の値1でそれを置くHashMap<int[], Long>各発生をカウントし、のは、それを呼びましょうdistinctElements

  • ハッシュである場合、distinctHash対応アレイが中に含まれている場合、チェックrowListそうである場合、の値インクリメントdistinctElementsに見出される同一のアレイに関連rowListを(あなたがキーとして新しい配列を使用している場合は、その参照が異なっているので、あなたは別のキーを作成します)。

ここで返されたブール値は、新規の異なる配列が見つかった場合、私は私のすべてのアレイに順次この機能を適用するよう指示コードは、次のとおりです。

    HashMap<int[], Long> distinctElements;
    HashMap<Integer, ArrayList<int[]>> distinctHash;

    private boolean addRow(int[] row) {

        if (distinctHash.containsKey(hash)) {
            int[] indexRow = distinctHash.get(hash).get(0);
            for (int[] previousRow: distinctHash.get(hash)) {
                if (Arrays.equals(previousRow, row)) {
                    distinctElements.put(
                            indexRow,
                            distinctElements.get(indexRow) + 1
                    );
                    return false;
                }
            }
            distinctElements.put(row, 1L);

            ArrayList<int[]> rowList = distinctHash.get(hash);
            rowList.add(row);
            distinctHash.put(hash, rowList);

            return true;

        } else {
            distinctElements.put(row, 1L);

            ArrayList<int[]> newValue = new ArrayList<>();
            newValue.add(row);
            distinctHash.put(hash, newValue);

            return true;
        }
    }

質問

問題は、私のアルゴリズムが自分のニーズ(5,000,000アレイ用の40代、および20,000,000アレイ用の2H-3H)のために遅すぎるということです。NetBeansのでプロファイリングハッシュは(Googleのグアバmurmur3_128ハッシュ関数を使用して)実行時の70%を取ることを教えてくれました。

速いかもしれない別のアルゴリズムはありますか?私が言ったように私は彼らの出現数で、配列の値に興味がないんです。私は確率的アルゴリズムで結構ですので、スピードのための精度を犠牲にする準備ができています。

アンドレアス:

ラップint[]実装するクラスにequalsしてhashCode構築した後、Mapインスタンス数にラッパークラスの。

class IntArray {
    private int[] array;
    public IntArray(int[] array) {
        this.array = array;
    }
    @Override
    public int hashCode() {
        return Arrays.hashCode(this.array);
    }
    @Override
    public boolean equals(Object obj) {
        return (obj instanceof IntArray && Arrays.equals(this.array, ((IntArray) obj).array));
    }
    @Override
    public String toString() {
        return Arrays.toString(this.array);
    }
}

テスト

int[][] input = {{1,2,3},
                 {1,0,3},
                 {1,2,3},
                 {5,2,6},
                 {5,2,6},
                 {5,2,6}};
Map<IntArray, Long> map = Arrays.stream(input).map(IntArray::new)
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
map.entrySet().forEach(System.out::println);

出力

[1, 2, 3]=2
[1, 0, 3]=1
[5, 2, 6]=3

注:上記のソリューションは、より速く、少ないメモリの使用であるラビンドラRanwalaにより溶液が、それは優れている議論の余地があるので、それは、余分なクラスを作成する必要がありません。

小さなアレイに対して、ラビンドラRanwalaによって以下簡単な解決策を使用します。
より大きなアレイの場合、上記溶液は、おそらく優れています。

 Map<List<Integer>, Long> map = Stream.of(input)
         .map(a -> Arrays.stream(a).boxed().collect(Collectors.toList()))
         .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=225107&siteId=1