How to Count Duplicate Elements in Arraylist

1. Overview

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

2. Loop with 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.

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.

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

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()

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

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.

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.

4. Loop with 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.

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

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.

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

5. Stream 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.

The toMap() collector helps us to convert the input list into a 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.

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

Except for the toMap(), our problem can be solved by two other collectors, groupingBy() and 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.

7. Conclusion

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.

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

Related posts:

Java Program to Describe the Representation of Graph using Incidence List
The Order of Tests in JUnit
Java InputStream to String
Java Program to Find the Peak Element of an Array O(n) time (Naive Method)
Java Program to Implement Queue using Linked List
Introduction to the Java NIO2 File API
Show Hibernate/JPA SQL Statements from Spring Boot
Java Program to Find Location of a Point Placed in Three Dimensions Using K-D Trees
Java Program to Find the Nearest Neighbor Using K-D Tree Search
Spring MVC Async vs Spring WebFlux
Java Program to Implement Heap Sort Using Library Functions
Java Program to Construct an Expression Tree for an Infix Expression
Java Program to Implement Gale Shapley Algorithm
Spring Boot Change Context Path
Java Program to Evaluate an Expression using Stacks
Reading an HTTP Response Body as a String in Java
Period and Duration in Java
Java Program to Implement LinkedBlockingQueue API
Creating a Generic Array in Java
Java Program to Find the GCD and LCM of two Numbers
LinkedHashSet trong java
Predicate trong Java 8
Java Program to Perform Postorder Recursive Traversal of a Given Binary Tree
Phương thức forEach() trong java 8
Lớp lồng nhau trong java (Java inner class)
Rest Web service: Filter và Interceptor với Jersey 2.x (P1)
Guide to @ConfigurationProperties in Spring Boot
New Features in Java 14
A Custom Media Type for a Spring REST API
Java Program to Implement Splay Tree
Java Program to Search for an Element in a Binary Search Tree
Java Program to Find kth Largest Element in a Sequence