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;
}

知らせ (k、v)-> v == null? 1L:v + 1L を実装する再マッピング機能です BiFunction インターフェース。 特定のキーについて、1ずつ増加した現在の値を返すか(キーがマップにすでに存在する場合)、デフォルト値の1を返します。

コードを読みやすくするために、再マッピング関数をその変数に抽出するか、countByForEachLoopWithMapCompute。の入力パラメーターとして使用することもできます。

4. Map.merge()でループする

Map.compute()を使用する場合、たとえば、特定のキーのマッピングが存在しない場合、null値を明示的に処理する必要があります。これがnull[を実装した理由です。 X177X]再マッピング機能をチェックインします。 しかし、これはきれいに見えません。

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から入手できます。