Find Duplicate Characters in a String in Java
Problem
A duplicate character is one that appears two or more times in a string, as opposed to a character that appears exactly once.
Given a string, find every character that appears more than once.
Java Program
import java.util.LinkedHashMap;
import java.util.Map;
public class FindDuplicateCharacters {
public static void main(String[] args) {
String str = "programming";
Map<Character, Integer> freq = new LinkedHashMap<>();
for (char c : str.toCharArray()) {
freq.put(c, freq.getOrDefault(c, 0) + 1); // reads the current count, or 0 if unseen, then increments it
}
StringBuilder duplicates = new StringBuilder();
for (Map.Entry<Character, Integer> entry : freq.entrySet()) {
if (entry.getValue() > 1) { // only keep characters whose count is more than one
if (duplicates.length() > 0) duplicates.append(", ");
duplicates.append(entry.getKey());
}
}
System.out.println("Duplicate characters: " + duplicates);
}
}Output
Core Logic
Building a full frequency map first, then keeping only the entries with a count greater than one, separates 'how often' from 'which ones repeat'.
- 1A
LinkedHashMap<Character, Integer>namedfreqtracks each character's count, exactly like counting character frequency. - 2
freq.getOrDefault(c, 0)reads the current count, or0if unseen, thenfreq.put(c, ...)stores the incremented count. - 3After the full scan,
entrySet()is walked once more, checkingentry.getValue() > 1for each character. - 4Every character whose count exceeds one is appended to the result, in the order it was first seen.
"programming", 'p', 'o', 'a', 'i', and 'n' each appear once, while 'r', 'g', and 'm' each appear twice — so the duplicates reported are r, g, m.Key Point: This is the same frequency map used to count character occurrences — finding duplicates is just a filter applied on top of it.
Why: Building the frequency map visits every character once and holds one entry per distinct character, up to n in the worst case.
Key Concepts
Approach 2: Java 8
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class FindDuplicateCharactersStream {
public static void main(String[] args) {
String str = "programming";
Map<Character, Long> freq = str.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(c -> c, LinkedHashMap::new, Collectors.counting()));
// Keeps only the characters whose count is greater than one
String duplicates = freq.entrySet().stream()
.filter(e -> e.getValue() > 1)
.map(Map.Entry::getKey)
.map(String::valueOf)
.collect(Collectors.joining(", "));
System.out.println("Duplicate characters: " + duplicates);
}
}
Output
Core Logic
groupingBy plus counting() builds the same frequency map declaratively, and a stream filter keeps only the entries with a count above one.
- 1
str.chars().mapToObj(c -> (char) c)turns the string's character codes into aStream<Character>. - 2
Collectors.groupingBy(c -> c, LinkedHashMap::new, Collectors.counting())groups equal characters and counts each group, in first-appearance order. - 3
freq.entrySet().stream().filter(e -> e.getValue() > 1)keeps only the entries whose count is greater than one. - 4
.map(Map.Entry::getKey)pulls just the character out of each surviving entry, andCollectors.joining(", ")joins them into the final result.
"programming" and filtering for counts above one keeps r, g, and m, the same duplicates the manual version found.Key Point: The filter condition — count > 1 — is the exact same rule the manual version's second loop checks, just expressed as a stream predicate instead of an if statement.
Why: groupingBy() still visits every character once while building the map, which holds one entry per distinct character just like the manual version.