如何计算数组列表中的重复元素
1.概述
在这个简短的教程中,我们将研究一些不同的方法来计算ArrayList中重复的元素。
2.用Map.put()进行循环。
我们预期的结果将是一个Map对象,它包含了输入列表中的所有元素作为键,以及每个元素的计数作为值。
实现这一目标的最直接的解决方案是循环浏览输入列表,并对每个元素进行处理。
- 如果resultMap包含该元素,我们就将一个计数器增加1。
- 否则,我们输入一个新的条目(element, 1)到Map上。
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中,Map接口中引入了方便的compute()方法。我们也可以利用这个方法。
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>接口。对于一个给定的键,它要么返回它的当前值加1(如果该键已经存在于地图中),要么返回默认值1。
为了使代码更具可读性,我们可以将重映射函数提取到其变量中,或者甚至将其作为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. Stream 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. Stream API Collectors.groupingBy() 和 Collectors.counting()
除了toMap(),我们的问题可以通过另外两个收集器来解决,groupingBy()和counting()。
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上获得,。