Group Anagrams in Java Using HashMap – Program and Examples

In this tutorial, I will demonstrate how to group anagrams from a List of strings in Java. This is a popular coding interview problem that helps you understand HashMap, string manipulation, sorting, and character frequency counting.

Two strings are anagrams if they contain the same characters with the same frequencies, but the characters may appear in a different order. For example, "eat", "tea", and "ate" are anagrams because each string contains the letters 'a', 'e', and 't' exactly once.

The goal is to take a collection of strings and place all anagrams into the same group. Strings that do not have any matching anagrams should appear in their own groups.

In this tutorial, you will learn two approaches to grouping anagrams in Java. The first uses sorting and HashMap, while the second uses character frequency counting. I will also explain each approach step by step, provide a dry run, compare time complexity, and discuss important edge cases.

1. Problem Statement

Write a Java program to group anagrams from a List of strings.

Each group should contain strings that are anagrams of one another.

Example 1

Input:
["eat", "tea", "tan", "ate", "nat", "bat"]

Output:
[
  [eat, tea, ate],
  [tan, nat],
  [bat]
]

Explanation:

  • "eat", "tea", and "ate" belong to the same group.
  • "tan" and "nat" belong to another group.
  • "bat" has no matching anagram, so it forms its own group.

Example 2

Input:
["abc", "bca", "cab", "dog", "god", "cat"]

Output:
[
  [abc, bca, cab],
  [dog, god],
  [cat]
]

The order of groups does not affect whether the grouping is logically correct. However, the implementations in this tutorial use LinkedHashMap to produce predictable group order based on the first appearance of each group.

2. What Is an Anagram?

An anagram is formed by rearranging the characters of another string without adding or removing characters.

For two strings to be anagrams, they must contain the same characters with identical frequencies.

Consider these examples:

First String Second String Anagrams? Explanation
listen silent Yes Same characters and frequencies
eat tea Yes Same letters in different order
hello world No Different characters
aab abb No Character frequencies differ

Understanding this property is the key to solving the group anagrams problem.

3. Method 1: Group Anagrams Using Sorting and HashMap

The most common approach is to sort the characters of each string and use the sorted result as a key in a Map.

Anagram strings contain the same characters, so sorting their characters produces the same result.

For example:

Original String    Sorted Characters

eat                aet
tea                aet
ate                aet

tan                ant
nat                ant

bat                abt

Notice that all anagrams produce identical sorted keys.

The program can therefore use each sorted key to identify the correct group.

How the Algorithm Works

  1. Create a Map where the key is a sorted string and the value is a List of matching anagrams.
  2. Iterate through each string in the input list.
  3. Convert the string into a character array.
  4. Sort the character array using Arrays.sort().
  5. Convert the sorted characters into a String to create a grouping key.
  6. Add the original string to the List associated with that key.
  7. Return all the grouped Lists.

Java Program Using HashMap and Sorting

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class GroupAnagrams {

    /**
     * Groups anagram strings together.
     *
     * @param words input list of strings
     * @return list containing groups of anagrams
     */
    public static List<List<String>> groupAnagrams(
            List<String> words) {

        Map<String, List<String>> anagramMap =
                new LinkedHashMap<>();

        for (String word : words) {

            // Convert string to character array
            char[] characters = word.toCharArray();

            // Sort characters to create a common key
            Arrays.sort(characters);

            String key = new String(characters);

            // Add the original word to its group
            anagramMap.computeIfAbsent(
                    key, k -> new ArrayList<>()
            ).add(word);
        }

        return new ArrayList<>(anagramMap.values());
    }

    public static void main(String[] args) {

        List<String> words = Arrays.asList(
                "eat", "tea", "tan", "ate", "nat", "bat"
        );

        List<List<String>> result =
                groupAnagrams(words);

        System.out.println("Original List: " + words);
        System.out.println("Grouped Anagrams:");

        for (List<String> group : result) {
            System.out.println(group);
        }
    }
}

Output

Original List: [eat, tea, tan, ate, nat, bat]
Grouped Anagrams:
[eat, tea, ate]
[tan, nat]
[bat]

Note: The program uses LinkedHashMap, which extends HashMap and preserves the insertion order of groups. If you use a regular HashMap, the group order is not guaranteed.

4. Step-by-Step Explanation

Step 1: Create a Map for Anagram Groups

Map<String, List<String>> anagramMap =
        new LinkedHashMap<>();

The Map stores sorted character sequences as keys and their corresponding anagram groups as values.

For example, the key "aet" identifies all strings containing exactly the letters 'a', 'e', and 't'.

Step 2: Iterate Through Each String

for (String word : words) {

The enhanced for loop processes each word from the input list.

Step 3: Convert the String into a Character Array

char[] characters = word.toCharArray();

The toCharArray() method converts the string into a character array.

For example, "tea" becomes:

[t, e, a]

Step 4: Sort the Characters

Arrays.sort(characters);

The Arrays.sort() method sorts the characters in ascending order.

After sorting:

[a, e, t]

The strings "eat", "tea", and "ate" all produce this same sorted character array.

Step 5: Create the Grouping Key

String key = new String(characters);

This converts the sorted character array into a String.

For all three anagrams "eat", "tea", and "ate", the generated key is "aet".

Step 6: Add the Word to the Correct Group

anagramMap.computeIfAbsent(
        key, k -> new ArrayList<>()
).add(word);

The computeIfAbsent() method checks whether the key already has a corresponding List.

  • If the key is absent, it creates a new ArrayList.
  • If the key exists, it retrieves the existing ArrayList.
  • The original word is added to that List.

For example, after processing the strings "eat", "tea", and "ate", the map contains the following logical entry:

aet = [eat, tea, ate]

Step 7: Return the Anagram Groups

return new ArrayList<>(anagramMap.values());

The values() method returns the Lists stored in the Map.

Creating a new ArrayList produces the required List<List<String>> result.

5. Dry Run: Grouping Anagrams

Consider this input:

["eat", "tea", "tan", "ate", "nat", "bat"]

The program processes each word, sorts its characters, and adds it to the matching group.

Step Word Sorted Key Updated Group
1 eat aet [eat]
2 tea aet [eat, tea]
3 tan ant [tan]
4 ate aet [eat, tea, ate]
5 nat ant [tan, nat]
6 bat abt [bat]

After all strings have been processed, the Map logically contains:

aet = [eat, tea, ate]
ant = [tan, nat]
abt = [bat]

The final result is:

[
  [eat, tea, ate],
  [tan, nat],
  [bat]
]

6. Method 2: Group Anagrams Using Character Frequency

Another approach is to count the frequency of every character in each string.

Two strings are anagrams if they contain the same characters with identical frequencies.

For example:

String: eat
a = 1
e = 1
t = 1

String: tea
a = 1
e = 1
t = 1

Because the frequency information is identical, both strings belong to the same group.

For this approach, assume that each input string contains only lowercase English letters from 'a' to 'z'.

You can represent the character frequencies using an integer array of length 26.

Java Program Using Character Frequency

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class GroupAnagramsUsingFrequency {

    public static List<List<String>> groupAnagrams(
            List<String> words) {

        Map<String, List<String>> anagramMap =
                new LinkedHashMap<>();

        for (String word : words) {

            int[] frequency = new int[26];

            // Count frequency of each character
            for (char ch : word.toCharArray()) {
                frequency[ch - 'a']++;
            }

            // Build a unique key from character counts
            StringBuilder keyBuilder = new StringBuilder();

            for (int count : frequency) {
                keyBuilder.append('#').append(count);
            }

            String key = keyBuilder.toString();

            // Group strings with the same frequency key
            anagramMap.computeIfAbsent(
                    key, k -> new ArrayList<>()
            ).add(word);
        }

        return new ArrayList<>(anagramMap.values());
    }

    public static void main(String[] args) {

        List<String> words = Arrays.asList(
                "eat", "tea", "tan", "ate", "nat", "bat"
        );

        List<List<String>> result =
                groupAnagrams(words);

        System.out.println("Grouped Anagrams:");

        for (List<String> group : result) {
            System.out.println(group);
        }
    }
}

Output

Grouped Anagrams:
[eat, tea, ate]
[tan, nat]
[bat]

How the Frequency-Based Approach Works

For every input string, the algorithm creates a frequency array:

int[] frequency = new int[26];

Each position represents one lowercase English letter.

For example:

  • Index 0 represents 'a'.
  • Index 1 represents 'b'.
  • Index 2 represents 'c'.
  • Index 25 represents 'z'.

The statement:

frequency[ch - 'a']++;

increments the count associated with the current character.

Next, the program converts the frequency array into a string key.

The separator character # is important because it prevents ambiguity between adjacent frequency values.

For example, the sequences of counts [1, 11] and [11, 1] should not generate the same key.

Using separators produces distinct representations such as #1#11 and #11#1.

Strings with identical frequency keys are then placed in the same group.

Important Limitation

This frequency-based implementation supports lowercase English letters only.

It does not directly support uppercase letters, spaces, punctuation, or arbitrary Unicode characters.

For those inputs, use the sorting-based approach or redesign the frequency representation.

7. Time and Space Complexity

Let's compare the sorting-based and character-frequency approaches.

Assume:

  • n is the number of strings.
  • k is the maximum length of a string.
  • u is the number of unique anagram groups.
Approach Expected Time Additional Space Best Use Case
Sorting + HashMap O(n × k log k) O(n × k) General-purpose solution
Frequency + HashMap O(n × (k + 26)) O(n + u × 26) Lowercase English letters

These bounds assume efficient average-case hash-map operations and refer to additional storage, including grouping keys and result lists.

Sorting-Based Approach

Sorting a string containing k characters requires O(k log k) time in the general case.

Since there are n strings, the total expected time complexity is:

O(n × k log k)

The map stores the original string references in groups and maintains sorted-character keys, resulting in O(n × k) additional space in the worst case.

Frequency-Based Approach

The frequency approach scans each string and counts its characters.

For lowercase English letters, it also builds a fixed-size key containing 26 counts.

Its expected time complexity is:

O(n × (k + 26))

Because 26 is constant, this is commonly simplified to O(n × k) for nonempty strings.

The frequency approach avoids sorting and can be more efficient for long strings containing only lowercase English letters.

8. Handling Important Edge Cases

Case 1: Empty Input List

Input:
[]

Output:
[]

The program returns an empty List because there are no strings to group.

Case 2: Single String

Input:
["java"]

Output:
[[java]]

A string without matching anagrams forms its own group.

Case 3: All Strings Are Anagrams

Input:
["abc", "bca", "cab"]

Output:
[[abc, bca, cab]]

All strings belong to one group because their characters have the same frequencies.

Case 4: No Strings Are Anagrams

Input:
["cat", "dog", "fish"]

Output:
[[cat], [dog], [fish]]

Each string appears in its own group.

Case 5: Duplicate Strings

Input:
["eat", "tea", "eat", "ate"]

Output:
[[eat, tea, eat, ate]]

Duplicate strings are preserved because the task is to group anagrams, not remove duplicates.

Case 6: Empty Strings

Input:
["", "a", ""]

Output:
[["", ""], ["a"]]

Two empty strings are anagrams because both contain zero characters.

The sorting-based implementation handles empty strings because sorting an empty character array produces an empty grouping key.

Case 7: Uppercase and Lowercase Letters

Input:
["Eat", "tea", "ate"]

Output:
[[Eat], [tea, ate]]

By default, the sorting-based approach is case-sensitive.

Therefore, uppercase 'E' and lowercase 'e' are treated as different characters.

If you need case-insensitive grouping, normalize strings consistently before generating their keys.

Case 8: Null Strings

The implementations assume that every element in the input List is a non-null String.

Passing a null element causes a NullPointerException when the program calls toCharArray().

If null elements are possible in your application, either validate and reject them or define a separate rule for grouping null values.

9. Common Mistakes to Avoid

Mistake 1: Comparing Strings Only by Their Length

Two strings having the same length does not mean they are anagrams.

For example, "abc" and "xyz" have equal lengths but contain different characters.

You must compare their sorted characters or their character frequencies.

Mistake 2: Using an Ambiguous Frequency Key

When generating a key from character frequencies, avoid concatenating counts without separators.

For example, count sequences [1, 11] and [11, 1] could both produce the same text if you simply concatenate their values.

Use a separator or another unambiguous representation.

Mistake 3: Forgetting to Create a New List for Each Group

Each distinct anagram key must have its own List.

The computeIfAbsent() method makes this easy by creating a List only when the key is missing.

Mistake 4: Assuming HashMap Preserves Group Order

HashMap does not guarantee iteration order.

If your program needs predictable group order, use LinkedHashMap or explicitly sort the result.

Mistake 5: Assuming Character Frequency Works for Every Input

An integer array of length 26 supports only lowercase English letters when indexed using ch - 'a'.

Uppercase letters and other characters may produce invalid indexes.

Use the sorting-based approach when the input character set is not restricted to lowercase English letters.

10. Frequently Asked Questions

Q1. What is the group anagrams problem in Java?

The group anagrams problem asks you to organize a List of strings so that strings containing the same characters with identical frequencies belong to the same group.

Q2. What is the easiest way to group anagrams in Java?

One of the easiest approaches is to sort each string's characters and use the sorted result as a key in a HashMap.

Q3. Why is HashMap used for grouping anagrams?

HashMap efficiently associates a grouping key with a List of matching strings. Strings that produce the same key are added to the same group.

Q4. Can I group anagrams without sorting the strings?

Yes. For lowercase English letters, you can count the frequency of each character and use the resulting frequency representation as the key.

Q5. What is the time complexity of grouping anagrams using sorting?

The expected time complexity is O(n × k log k), where n is the number of strings and k is the maximum string length.

Q6. What is the time complexity of the frequency-based approach?

For lowercase English letters, the expected time complexity is O(n × (k + 26)), commonly simplified to O(n × k).

Q7. Does the order of anagram groups matter?

Most versions of the coding interview problem accept groups in any order. However, you should check the requirements of the specific problem.

Q8. Do duplicate strings remain in the result?

Yes. The provided programs preserve duplicate strings and place them in their corresponding anagram groups.

Q9. Which solution is better for Java coding interviews?

The sorting-based approach is generally the best starting point because it is simple and works for a broad range of string inputs.

The frequency-based approach is a useful optimization when the input is restricted to lowercase English letters.

11. Conclusion

In this tutorial, you learned how to group anagrams from a List of strings in Java using two different approaches.

  • Sorting with HashMap: Sorts the characters of each string and groups strings that produce identical sorted keys.
  • Character frequency with HashMap: Creates a frequency-based key without sorting, making it efficient for lowercase English-letter inputs.

For Java coding interviews, I recommend learning the sorting-based solution first. It is straightforward to implement, easy to explain, and demonstrates how HashMap can organize related data efficiently.

Once you understand the basic approach, practice the frequency-based solution. It helps you recognize an important optimization: when the character set is limited, counting character occurrences can be more efficient than sorting.

The same techniques are useful in other Java interview problems involving string anagrams, character frequency counting, duplicate detection, and HashMap-based grouping.

12. Official Java Documentation

For additional information about the Java APIs used in this tutorial, refer to the official Oracle documentation:

Continue Learning with Java Guides

Want to take your programming skills to the next level? Explore my practical Udemy courses on Java, Spring Boot, Microservices, Full-Stack Development, Docker, and Generative AI. You can also follow my structured learning roadmaps to become a professional developer.

By Ramesh Fadatare - Udemy Instructor Partner

Comments