Introduction

Finding duplicate characters in a string is a common Java programming problem that appears in technical interviews, coding assignments, and text-processing applications. It helps you understand character frequency analysis, collections, hashing, and efficient string traversal.

For example:

  • "hello" → Duplicate character: l
  • "programming" → Duplicate characters: r, g, m
  • "mississippi" → Duplicate characters: i, s, p

This guide explores four different approaches, from beginner-friendly solutions to modern Java Stream API implementations.

Advertisement

Method 1: Using HashMap (Character Frequency)

The most common approach is to store the frequency of each character using a HashMap. Any character whose count is greater than one is considered a duplicate.

 
import java.util.HashMap;
import java.util.Map;

public class DuplicateCharactersHashMap {

    public static void findDuplicates(String str) {

        if (str == null || str.isEmpty()) {
            System.out.println("Input string is empty.");
            return;
        }

        Map<Character, Integer> characterCount =
                new HashMap<>();

        // Count frequency of each character
        for (char ch : str.toCharArray()) {
            characterCount.put(
                    ch,
                    characterCount.getOrDefault(ch, 0) + 1
            );
        }

        System.out.println("Duplicate characters:");

        boolean found = false;

        for (Map.Entry<Character, Integer> entry :
                characterCount.entrySet()) {

            if (entry.getValue() > 1) {
                found = true;
                System.out.println(
                        entry.getKey() + " : " + entry.getValue());
            }
        }

        if (!found) {
            System.out.println("No duplicate characters found.");
        }
    }

    public static void main(String[] args) {

        findDuplicates("hello");
    }
}
 

Output

 
Duplicate characters:
l : 2
 

How It Works

  1. Traverse every character.
  2. Store its frequency inside a HashMap.
  3. Print entries whose frequency is greater than one.

Advantages

  • Works with Unicode characters.
  • Easy to understand.
  • Provides duplicate counts.
  • Suitable for most real-world applications.

Disadvantages

  • Requires extra memory for the map.

Time Complexity

O(n)

Space Complexity

O(k)

Where k is the number of unique characters.


Method 2: Using HashSet

A HashSet can quickly determine whether a character has already been seen.

 
import java.util.HashSet;
import java.util.Set;

public class DuplicateCharactersHashSet {

    public static void findDuplicates(String str) {

        if (str == null || str.isEmpty()) {
            System.out.println("Input string is empty.");
            return;
        }

        Set<Character> seen =
                new HashSet<>();

        Set<Character> duplicates =
                new HashSet<>();

        for (char ch : str.toCharArray()) {

            if (!seen.add(ch)) {
                duplicates.add(ch);
            }
        }

        System.out.println(
                "Duplicate characters: " + duplicates);
    }

    public static void main(String[] args) {

        findDuplicates("programming");
    }
}
 

Output

 
Duplicate characters: [r, g, m]
 

The order may vary because HashSet does not preserve insertion order.


Advantages

  • Very concise.
  • Efficient duplicate detection.
  • No frequency counting required.

Disadvantages

  • Doesn't provide occurrence counts.
  • Output order is unpredictable.

Maintaining Order

If you want duplicates in the same order they appear, use LinkedHashSet.

 
Set<Character> duplicates =
        new LinkedHashSet<>();
 

Time Complexity

O(n)

Space Complexity

O(k)


Method 3: Using Character Array (ASCII)

If the input contains only ASCII characters, an integer array provides the fastest solution.

 
public class DuplicateCharactersArray {

    public static void findDuplicates(String str) {

        if (str == null || str.isEmpty()) {
            System.out.println("Input string is empty.");
            return;
        }

        int[] count = new int[256];

        for (char ch : str.toCharArray()) {

            if (ch < 256) {
                count[ch]++;
            }
        }

        System.out.println("Duplicate characters:");

        boolean found = false;

        for (int i = 0; i < count.length; i++) {

            if (count[i] > 1) {

                found = true;

                System.out.println(
                        (char) i + " : " + count[i]);
            }
        }

        if (!found) {
            System.out.println("No duplicate characters found.");
        }
    }

    public static void main(String[] args) {

        findDuplicates("mississippi");
    }
}
 

Output

 
Duplicate characters:
i : 4
p : 2
s : 4
 

How It Works

Each character's ASCII value becomes the index of the array.

Example:

 
count['a']++
count['b']++
count['c']++
 

Advantages

  • Extremely fast.
  • Constant-time indexing.
  • Excellent for interview questions involving ASCII.

Disadvantages

  • Limited to ASCII.
  • Not suitable for Unicode characters.

Time Complexity

O(n)

Space Complexity

O(1)

The array size remains fixed at 256.


Method 4: Using Stream API

Modern Java (Java 8+) allows duplicate detection using Streams.

 
import java.util.stream.Collectors;

public class DuplicateCharactersStream {

    public static void main(String[] args) {

        String str = "java";

        str.chars()
           .boxed()
           .collect(Collectors.groupingBy(
                   c -> (char) c.intValue(),
                   Collectors.counting()))
           .entrySet()
           .stream()
           .filter(entry -> entry.getValue() > 1)
           .forEach(entry ->
                   System.out.println(
                           entry.getKey() +
                           " : " +
                           entry.getValue()));
    }
}
 

Output

 
a : 2
 

Advantages

  • Modern functional programming style.
  • Compact implementation.
  • Easy to integrate with stream pipelines.

Disadvantages

  • Less beginner-friendly.
  • Higher overhead than loops.
  • Harder to debug.

Time Complexity

O(n)

Space Complexity

O(k)


Handling Common Variations

Ignore Case

Convert the string to lowercase before processing.

 
str = str.toLowerCase();
 

Example:

 
"JavaJAVA"

↓

"javajava"
 

Duplicates:

 
j : 2
a : 4
v : 2
 

Ignore Spaces

 
str = str.replaceAll("\\s+", "");
 

Ignore Non-Letter Characters

 
for (char ch : str.toCharArray()) {

    if (!Character.isLetter(ch)) {
        continue;
    }

    // Count letters only
}
 

Performance Comparison

Method Time Complexity Space Complexity Unicode Support Frequency Count
HashMap O(n) O(k)
HashSet O(n) O(k)
Character Array O(n) O(1) ✘ (ASCII only)
Streams O(n) O(k)

Best Practices

Validate Input

 
if (str == null || str.isEmpty()) {
    return;
}
 

Ignore Case When Required

 
str = str.toLowerCase();
 

Use LinkedHashMap to Preserve Order

 
Map<Character, Integer> frequency =
        new LinkedHashMap<>();
 

This prints duplicates in the order they first appear.


Choose the Right Data Structure

  • ASCII input → Character array
  • Unicode input → HashMap
  • Need insertion order → LinkedHashMap
  • Need only duplicates → HashSet

Frequently Asked Questions

Q1: Should I count spaces as characters?

Answer: Yes, by default every character is counted. Skip whitespace using:

 
if (Character.isWhitespace(ch)) {
    continue;
}
 

Q2: Which method is fastest?

Answer: The character array approach is the fastest for ASCII input. For Unicode strings, HashMap offers the best balance between speed and flexibility.


Q3: How do I ignore case?

Answer: Convert the string to lowercase (or uppercase) before counting.

 
str = str.toLowerCase();
 

Q4: How do I find the first duplicate character?

Answer: Traverse the string while maintaining a HashSet. The first character that cannot be added to the set is the first duplicate.

 
Set<Character> seen = new HashSet<>();

for (char ch : str.toCharArray()) {

    if (!seen.add(ch)) {
        System.out.println(ch);
        break;
    }
}
 

Q5: What about special characters?

Answer: All approaches treat special characters as normal characters unless you explicitly filter them out.


Q6: How do I print duplicates in insertion order?

Answer: Use LinkedHashMap or LinkedHashSet instead of their regular counterparts.


Q7: Can these methods handle Unicode?

Answer: Yes. HashMap, HashSet, and Streams work correctly with Unicode characters. The character-array approach is suitable only for ASCII.


Q8: How do these methods perform on very large strings?

Answer: All loop-based methods run in O(n) time. The character-array method is the fastest for ASCII, while HashMap scales well for Unicode.


Q9: How do I exclude digits or punctuation?

Answer: Add a condition before counting.

 
if (!Character.isLetter(ch)) {
    continue;
}
 

Q10: How do I ignore whitespace characters?

Answer: Skip them during iteration.

 
if (Character.isWhitespace(ch)) {
    continue;
}