Arraylistの重複要素をカウントする方法
1. 概要
この短いチュートリアルでは、ArrayList内の重複した要素をカウントするいくつかの異なる方法を見ていきます。
2. Map.put()でループする
期待される結果は、 Map オブジェクトになります。これには、入力リストのすべての要素がキーとして含まれ、各要素の数が値として含まれます。
これを実現するための最も簡単な解決策は、入力リストをループして各要素を処理することです。
- resultMap に要素が含まれている場合、カウンターを1つインクリメントします
- それ以外の場合は、新しいマップエントリ(element、1)をマップに配置します。
public <T> Map<T, Long> countByClassicalLoop(List<T> inputList) {
Map<T, Long> resultMap = new HashMap<>();
for (T element : inputList) {
if (resultMap.containsKey(element)) {
resultMap.put(element, resultMap.get(element) + 1L);
} else {
resultMap.put(element, 1L);
}
}
return resultMap;
}
この実装は、最新のすべてのJavaバージョンで機能するため、最高の互換性があります。
Java 8より前の互換性が必要ない場合は、メソッドをさらに簡略化できます。
public <T> Map<T, Long> countByForEachLoopWithGetOrDefault(List<T> inputList) {
Map<T, Long> resultMap = new HashMap<>();
inputList.forEach(e -> resultMap.put(e, resultMap.getOrDefault(e, 0L) + 1L));
return resultMap;
}
次に、メソッドをテストするための入力リストを作成しましょう。
private List<String> INPUT_LIST = Lists.list(
"expect1",
"expect2", "expect2",
"expect3", "expect3", "expect3",
"expect4", "expect4", "expect4", "expect4");
そして今それを確認しましょう:
private void verifyResult(Map<String, Long> resultMap) {
assertThat(resultMap)
.isNotEmpty().hasSize(4)
.containsExactly(
entry("expect1", 1L),
entry("expect2", 2L),
entry("expect3", 3L),
entry("expect4", 4L));
}
このテストハーネスは、残りのアプローチで再利用します。
3. Map.compute()でループする
Java 8では、便利な compute()メソッドがMapインターフェースに導入されました。 この方法も利用できます。
public <T> Map<T, Long> countByForEachLoopWithMapCompute(List<T> inputList) {
Map<T, Long> resultMap = new HashMap<>();
inputList.forEach(e -> resultMap.compute(e, (k, v) -> v == null ? 1L : v + 1L));
return resultMap;
}
知らせ
コードを読みやすくするために、再マッピング関数をその変数に抽出するか、countByForEachLoopWithMapCompute。の入力パラメーターとして使用することもできます。
4. Map.merge()でループする
Map.compute()を使用する場合、たとえば、特定のキーのマッピングが存在しない場合、null値を明示的に処理する必要があります。これが
Map.merge()メソッドを使用して、コードをさらにクリーンアップしましょう。
public <T> Map<T, Long> countByForEachLoopWithMapMerge(List<T> inputList) {
Map<T, Long> resultMap = new HashMap<>();
inputList.forEach(e -> resultMap.merge(e, 1L, Long::sum));
return resultMap;
}
これで、コードはクリーンで簡潔に見えます。
merge()のしくみを説明しましょう。特定のキーのマッピングが存在しない場合、またはその値が null の場合、キーは指定された値に関連付けられます。 それ以外の場合は、再マッピング機能を使用して新しい値を計算し、それに応じてマッピングを更新します。
今回は使用したことに注意してくださいロング::合計として BiFunction
5. ストリームAPICollectors.toMap()
Java 8についてはすでに説明したので、強力なStreamAPIを忘れることはできません。 Stream APIのおかげで、非常にコンパクトな方法で問題を解決できます。
toMap()コレクターは、入力リストをMapに変換するのに役立ちます。
public <T> Map<T, Long> countByStreamToMap(List<T> inputList) {
return inputList.stream().collect(Collectors.toMap(Function.identity(), v -> 1L, Long::sum));
}
toMap()は便利なコレクターであり、ストリームをさまざまなMap実装に変換するのに役立ちます。
6. ストリームAPICollectors.groupingBy()および Collectors.counting()
toMap()を除いて、この問題は、 groupingBy()と counting()の2つのコレクターによって解決できます。
public <T> Map<T, Long> countByStreamGroupBy(List<T> inputList) {
return inputList.stream().collect(Collectors.groupingBy(k -> k, Collectors.counting()));
}
Java 8 Collectors を適切に使用すると、コードがコンパクトで読みやすくなります。
7. 結論
この簡単な記事では、リスト内の重複要素の数を計算するさまざまな方法を説明しました。
ArrayList自体をブラッシュアップしたい場合は、リファレンス記事をチェックしてください。
いつものように、完全なソースコードはGitHubでから入手できます。