
Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:
Once the early-adopter seats are all used, the price will go up and stay at $33/year.
Last updated: April 4, 2025
In this short tutorial, we’ll look at some different ways to count the duplicated elements in an ArrayList.
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:
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.
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.
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 the 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.
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.
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.
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.