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:

@Lookup Annotation in Spring
Java Program to Perform Naive String Matching
Java Program to find the maximum subarray sum O(n^2) time(naive method)
Java Program to Implement the String Search Algorithm for Short Text Sizes
Java Program to Use the Bellman-Ford Algorithm to Find the Shortest Path
Đồng bộ hóa các luồng trong Java
ThreadPoolTaskExecutor corePoolSize vs. maxPoolSize
Guide to Guava Multimap
New Stream Collectors in Java 9
Java Program to Implement Self Balancing Binary Search Tree
Derived Query Methods in Spring Data JPA Repositories
How to Set TLS Version in Apache HttpClient
Java Program to Implement TreeMap API
Java Program to Perform Postorder Non-Recursive Traversal of a Given Binary Tree
Java Program to Check if a Given Set of Three Points Lie on a Single Line or Not
Intro to Inversion of Control and Dependency Injection with Spring
Versioning a REST API
Tránh lỗi NullPointerException trong Java như thế nào?
Convert char to String in Java
Cơ chế Upcasting và Downcasting trong java
Java Program to Implement Hash Tables Chaining with Doubly Linked Lists
Find the Registered Spring Security Filters
Spring Boot - Sending Email
TreeSet và sử dụng Comparable, Comparator trong java
Java Program to Implement Slicker Algorithm that avoids Triangulation to Find Area of a Polygon
Merging Two Maps with Java 8
So sánh HashMap và Hashtable trong Java
Java Web Services – JAX-WS – SOAP
Quick Guide to Spring Bean Scopes
Introduction to Liquibase Rollback
Netflix Archaius with Various Database Configurations
A Guide to Java SynchronousQueue