Back to Stream: Grouping & Partitioning
Q22Question 22 of 22

Find duplicate elements in an integer list using groupingBy + counting

Key Concepts
  • groupingBy(n -> n, counting()) builds a Map<Integer, Long> of value to frequency
  • Filter entries with count greater than 1 to find duplicate values
  • counting() returns Long, so compare against the long literal 1L
  • For a simple yes/no 'are there duplicates', a HashSet add-and-check is faster
  • Use LinkedHashMap to get duplicates in first-seen order
  • The count-then-filter pattern is the same as finding duplicate characters, applied to integers

Definition

Count each integer with groupingBy(n -> n, counting()), then filter entries with count greater than 1 to find the values that appear more than once.
  • groupingBy(n -> n, counting()) builds Map of 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?

JAVA
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"));
    }
}
TEXT
Duplicates: [50000, 125000]
Rs.50000 appears 2 times
Rs.125000 appears 2 times

When is a HashSet faster for duplicate detection?

JAVA
// 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?

JAVA
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?

DIAGRAM
| 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?

JAVA
// 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.

JAVA
// 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.

JAVA
// 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

DIAGRAM
  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.

---

Share
Use ← → arrow keys to navigate