如何計算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上找到。