問題
私は、配列のリストを持っていると私は重複の発生をカウントします。
例えば、私はこれを持っている場合:
{{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()));