如何計算Arraylist中的重複元素

1.概述

在這個簡短的教程中,我們將研究一些不同的方法來計數ArrayList的重複元素。

2.使用Map.put()循環

我們的預期結果將是Map對象,該對象包含輸入列表中的所有元素作為鍵,而每個元素的計數作為值。

實現此目的最直接的解決方案是遍歷輸入列表和每個元素:

  • 如果resultMap包含元素,我們將計數器增加1
  • 否則,我們在HashMap上put一個新的Map條目(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<T, Long, Long>接口的重映射函數。對於給定的鍵,它要么返回其當前值加一(如果該鍵已存在於映射中),要么返回默認值一。

為了使代碼更具可讀性,我們可以將重映射函數提取到其變量中,甚至可以將其作為countByForEachLoopWithMapCompute.的輸入參數countByForEachLoopWithMapCompute.

4.使用Map.merge()循環

使用Map.compute() ,我們必須顯式處理null值-例如,如果給定鍵的映射不存在。這就是為什麼我們在重新映射功能中實現了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 ,則它將鍵與提供的值關聯。否則,它將使用重映射功能計算新值並相應地更新映射。

注意,這次我們使用Long::sum作為BiFunction<T, Long, Long>接口的實現。

5.流API Collectors.toMap()

既然我們已經討論過Java 8,我們就不會忘記強大的Stream API。借助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.流API Collectors.groupingBy()Collectors.counting()

除了toMap() ,我們的問題還可以通過另外兩個收集器groupingBy()[counting()](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#counting--)

public <T> Map<T, Long> countByStreamGroupBy(List<T> inputList) {

 return inputList.stream().collect(Collectors.groupingBy(k -> k, Collectors.counting()));

 }

正確使用Java 8 Collector使我們的代碼緊湊且易於閱讀。

7.結論

在這篇快速文章中,我們說明了各種方法來計算列表中重複元素的數量。

如果您想複習ArrayList本身,可以查看參考文章。

與往常一樣,完整的源代碼可以在GitHub上找到