Definition
- groupingBy(n -> n, counting()) builds
Mapof value to frequency. - filter(count > 1) extracts only values appearing more than once.
- counting() returns Long for overflow safety.
- HashSet shortcut: for yes/no duplicate detection, adding to a Set and checking
add()return value is faster. - LinkedHashMap preserves first-seen order if the order of duplicates matters.
Priya at the National Bank of India tallies how many times each transaction amount appears in a batch and lists the amounts appearing more than once as possible double-postings.
Understanding the Concepts
What does this question ask?
The interviewer wants duplicate detection in an integer list. This tests the count-then-filter pattern with groupingBy and counting, and awareness of the simpler HashSet alternative.
How do you find duplicate integers?
package com.bank.service;
import java.util.*;
import java.util.stream.Collectors;
public class DuplicateIntegers {
public static void main(String[] args) {
List<Integer> amounts = List.of(
50000, 125000, 75000, 50000, 90000, 125000, 60000);
Map<Integer, Long> frequency = amounts.stream()
.collect(Collectors.groupingBy(
n -> n,
LinkedHashMap::new,
Collectors.counting()));
List<Integer> duplicates = frequency.entrySet().stream()
.filter(e -> e.getValue() > 1)
.map(Map.Entry::getKey)
.toList();
System.out.println("Duplicates: " + duplicates);
frequency.entrySet().stream()
.filter(e -> e.getValue() > 1)
.forEach(e -> System.out.println(
"Rs." + e.getKey() + " appears " + e.getValue() + " times"));
}
}
Duplicates: [50000, 125000]
Rs.50000 appears 2 times
Rs.125000 appears 2 times
When is a HashSet faster for duplicate detection?
// Just need yes/no: are there any duplicates?
Set<Integer> seen = new HashSet<>();
boolean hasDuplicates = amounts.stream()
.anyMatch(n -> !seen.add(n));
System.out.println("Has duplicates: " + hasDuplicates);
// Short-circuits on the first duplicate found
The HashSet approach is faster for a simple boolean answer because it short-circuits. The groupingBy approach is for when you need the actual duplicate values and their counts.
How do you get duplicates with their counts?
Map<Integer, Long> duplicateMap = frequency.entrySet().stream()
.filter(e -> e.getValue() > 1)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> a,
LinkedHashMap::new));
duplicateMap.forEach((val, count) ->
System.out.println(val + " -> " + count + " times"));
How is this different from finding duplicate characters?
| Aspect | Duplicate characters | Duplicate integers | |---|---|---| | Source | String.chars() -> Character | List<Integer> | | Classifier | c -> c (Character) | n -> n (Integer) | | Pre-processing | Case normalization, filter letters | None typically | | Pattern | Identical: count, then filter > 1 | Identical |
The count-then-filter pattern is exactly the same.
What about finding elements that appear exactly N times?
// Appears exactly 2 times
List<Integer> exactlyTwice = frequency.entrySet().stream()
.filter(e -> e.getValue() == 2)
.map(Map.Entry::getKey)
.toList();
// Appears 3 or more times
List<Integer> threeOrMore = frequency.entrySet().stream()
.filter(e -> e.getValue() >= 3)
.map(Map.Entry::getKey)
.toList();
What are common mistakes?
Mistake: using the HashSet approach when you need the actual duplicates.
// WRONG: anyMatch only gives boolean, not which values are duplicated
boolean hasDups = amounts.stream()
.anyMatch(n -> !seen.add(n)); // just true/false
// CORRECT: groupingBy + counting gives the actual duplicates
Map<Integer, Long> freq = amounts.stream()
.collect(Collectors.groupingBy(n -> n, Collectors.counting()));
Mistake: using Function.identity() vs n -> n.
// Both work; Function.identity() is more explicit
amounts.stream().collect(
Collectors.groupingBy(Function.identity(), Collectors.counting()));
amounts.stream().collect(
Collectors.groupingBy(n -> n, Collectors.counting()));
What are common interview follow-up questions?
- "How do you remove duplicates from a list?" Use distinct() or collect to a LinkedHashSet.
- "How do you find elements appearing in two lists?" That is intersection, not duplicate detection.
- "What is the time complexity?" O(n) for building the frequency map, O(d) for filtering where d is the number of distinct values.
Visual Overview
List: [50000, 125000, 75000, 50000, 90000, 125000, 60000]
│
│ groupingBy(n -> n, counting())
▼
┌──────────────────────────┐
│ 50000 → 2 │ ← duplicate
│ 125000 → 2 │ ← duplicate
│ 75000 → 1 │
│ 90000 → 1 │
│ 60000 → 1 │
└──────────────────────────┘
│
│ filter(count > 1)
▼
Duplicates: [50000, 125000]
Key Takeaway
Finding duplicate integers uses the same count-then-filter pattern as finding duplicate characters. Count each value with groupingBy and counting, then filter entries with count greater than one to get the duplicated values.
For a simple boolean "are there any duplicates", a HashSet with add-and-check is faster because it short-circuits on the first duplicate. Use groupingBy when the business needs the actual duplicated values and their counts, as in a reconciliation report. At the National Bank of India, Rajesh used this to surface transaction amounts appearing more than once as possible double-postings.
The count-then-filter shape is completely general: change the threshold to answer "appears exactly N times" or "appears 3 or more times". The classifier stays the same.
Remember one sentence: groupingBy(n -> n, counting()) builds a frequency map, and filtering for count > 1 extracts duplicates; for a boolean check, use HashSet.add.
---