How to Count Duplicate Elements in Arraylist – 如何计算数组表中的重复元素

最后修改: 2019年 10月 25日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this short tutorial, we’ll look at some different ways to count the duplicated elements in an ArrayList.

在这个简短的教程中,我们将研究一些不同的方法来计算ArrayList中重复的元素。

2. Loop with Map.put()

2.用Map.put()进行循环

Our expected result would be a Map object, which contains all elements from the input list as keys and the count of each element as value.

我们预期的结果将是一个Map对象,它包含输入列表中的所有元素作为键,每个元素的计数作为值。

The most straightforward solution to achieve this would be to loop through the input list and for each element:

实现这一目标的最直接的解决方案是循环浏览输入列表,并为每个元素。

  • if the resultMap contains the element, we increment a counter by 1
  • otherwise, we put a new map entry (element, 1) to the 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;
}

This implementation has the best compatibility, as it works for all modern Java versions.

这个实现具有最好的兼容性,因为它适用于所有现代的Java版本。

If we don’t need the pre-Java 8 compatibility, we can simplify our method further:

如果我们不需要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;
}

Next, let’s create an input list to test the method:

接下来,让我们创建一个输入列表来测试这个方法。

private List<String> INPUT_LIST = Lists.list(
  "expect1",
  "expect2", "expect2",
  "expect3", "expect3", "expect3",
  "expect4", "expect4", "expect4", "expect4");

And now let’s verify it:

现在让我们来验证一下。

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

We’ll reuse this test harness for the rest of our approaches.

我们将在其余的方法中重新使用这个测试线束。

3. Loop with Map.compute()

3.用Map.compute()进行循环

In Java 8, the handy compute() method has been introduced to the Map interface. We can make use of this method as well:

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

Notice (k, v) -> v == null ? 1L : v + 1L is the remapping function that implements the BiFunction<T, Long, Long> interface. For a given key, it either returns its current value incremented by one (if the key is already present in the map) or returns the default value of one.

注意(k, v) -> v == null ?1L : v + 1L是实现BiFunction<T, Long, Long>/em>接口的重映射函数。对于一个给定的键,它要么返回它的当前值加1(如果该键已经存在于地图中),要么返回默认值1。

To make the code more readable, we could extract the remapping function to its variable or even take it as the input parameter for the countByForEachLoopWithMapCompute.

为了使代码更具可读性,我们可以将重映射函数提取到其变量中,甚至将其作为countByForEachLoopWithMapCompute./strong>的输入参数。

4. Loop with Map.merge()

4.用Map.merge()进行循环

When using Map.compute(), we must handle the null values explicitly – for instance, if a mapping for a given key doesn’t exist. This is why we’ve implemented a null check in our remapping function. This, however, doesn’t look pretty.

当使用Map.compute()时,我们必须明确地处理null值–例如,如果某个键的映射不存在。这就是为什么我们在重映射函数中实现了null检查。然而,这看起来并不漂亮。

Let’s clean up our code further with the help of Map.merge() method:

让我们在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;
}

Now the code looks clean and concise.

现在,代码看起来简洁明了。

Let’s explain how merge() works. If the mapping for a given key doesn’t exist, or its value is null, it associates the key with the provided value. Otherwise, it calculates a new value using the remapping function and updates the mapping accordingly.

让我们解释一下merge()是如何工作的。如果给定的键的映射不存在,或者它的值是null,它将把该键与提供的值联系起来。否则,它会使用重映射函数计算一个新的值,并相应地更新映射。

Notice that this time we used Long::sum as the BiFunction<T, Long, Long> interface implementation.

注意,这次我们使用Long::sum作为BiFunction<T, Long, Long>接口的实现。

5. Stream API Collectors.toMap()

5.流API Collectors.toMap()

Since we’ve already talked about Java 8, we can’t forget the powerful Stream API. Thanks to the Stream API, we can solve the problem in a very compact way.

既然我们已经谈到了Java 8,我们就不能忘记强大的Stream API。多亏了Stream API,我们可以用一种非常紧凑的方式来解决问题。

The toMap() collector helps us to convert the input list into a Map:

toMap()收集器帮助我们将输入列表转换成Map

public <T> Map<T, Long> countByStreamToMap(List<T> inputList) {
    return inputList.stream().collect(Collectors.toMap(Function.identity(), v -> 1L, Long::sum));
}

The toMap() is a convenient collector, which can help us to transform the stream into different Map implementations.

toMap() 是一个方便的收集器,它可以帮助我们将流转化为不同的Map实现。

6. Stream API Collectors.groupingBy() and Collectors.counting()

6.流API Collectors.groupingBy()Collectors.counting()

Except for the toMap(), our problem can be solved by two other collectors, groupingBy() and counting():

除了toMap(),我们的问题可以通过另外两个收集器来解决,groupingBy()counting()

public <T> Map<T, Long> countByStreamGroupBy(List<T> inputList) {
    return inputList.stream().collect(Collectors.groupingBy(k -> k, Collectors.counting()));
}

The proper usage of Java 8 Collectors makes our code compact and easy to read.

正确使用Java 8 Collectors使我们的代码紧凑且易于阅读。

7. Conclusion

7.结语

In this quick article, we illustrated various ways to calculate the count of duplicate elements in a list.

在这篇快速文章中,我们说明了计算列表中重复元素数量的各种方法。

If you’d like to brush up on the ArrayList itself, you can check out the reference article.

如果你想了解ArrayList本身,你可以查看参考文章

As always, the complete source code is available over on GitHub.

一如既往,完整的源代码可在GitHub上获得