Valid Anagram in Java DSA: Sorting, HashMaps, and the Count Array Trick
-
Last Updated: August 2, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Solve Valid Anagram in Java DSA three ways — sorting, a HashMap counter, and the O(1) count array. Full step-by-step dry runs for beginners.
Valid Anagram in Java is one of those warm-up problems that hides a neat lesson about counting. Two words are anagrams when they use the exact same letters, just shuffled around. So “listen” and “silent” are anagrams, but “hello” and “world” are not.
The task is short. You get two strings. You return true if one is a rearrangement of the other, and false otherwise.
There is a small catch that trips people up. Length matters first. If the two strings have different lengths, they can never be anagrams, so you can bail out early.
We build the answer in three steps, like always. Sorting both strings is the easy first idea. A hash map that counts letters is the next step up. The count array of size 26 is the lean version interviewers love to see.
Every approach gets a full, step-by-step dry run on the same words. Nothing is skipped, so you can watch each letter change the counts and see exactly why the answer comes out true or false.
Let us pin down the rules before we write any code.
For our pair the answer is true, because both words are made of three a’s, one g, one m, one n, and one r. We will also trace a false pair, s = “anagram” and t = “nagaraz”, where a stray z spoils the match.
Two strings are anagrams when each letter shows up the same number of times in both. Order does not matter at all. So the whole problem boils down to comparing letter counts.
There are two clean ways to count letters. A hash map stores each character with its count, which works for any alphabet. A plain array of 26 slots works when the input is only lowercase a to z, and it is faster because array access is cheap.
Sort the letters of both strings, then compare them character by character. If two words are anagrams, their sorted forms are identical. It is the simplest idea to reach for, even if it is not the fastest.
if length(s) != length(t):
return false
a = sort(s) // sorted characters of s
b = sort(t) // sorted characters of t
return a equals bThe idea is simple. If two words are anagrams, sorting them gives the same result. So we sort both and check if they match.
import java.util.*;
public class ValidAnagramSort {
public static boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
char[] a = s.toCharArray();
char[] b = t.toCharArray();
Arrays.sort(a);
Arrays.sort(b);
return Arrays.equals(a, b);
}
public static void main(String[] args) {
System.out.println(isAnagram("anagram", "nagaram")); // true
System.out.println(isAnagram("anagram", "nagaraz")); // false
}
}This is the same plan, written in Java. Java cannot sort a String directly, so we first turn each String into a char array, which Arrays.sort can handle.
Let us trace the true pair first, s = “anagram” and t = “nagaram”. Both have length 7, so the length check passes and we move on to sorting.
Here is what each string looks like before and after Arrays.sort. Sorting arranges the letters alphabetically, so equal anagrams collapse to the exact same sequence.
| String | Before sort | After sort |
|---|---|---|
| s = anagram | a n a g r a m | a a a g m n r |
| t = nagaram | n a g a r a m | a a a g m n r |
Now compare the two sorted arrays slot by slot. Every position matches, so Arrays.equals returns true.
| Index | s sorted | t sorted | Match? |
|---|---|---|---|
| 0 | a | a | Yes |
| 1 | a | a | Yes |
| 2 | a | a | Yes |
| 3 | g | g | Yes |
| 4 | m | m | Yes |
| 5 | n | n | Yes |
| 6 | r | r | Yes |
Now the false pair, s = “anagram” and t = “nagaraz”. Lengths are still 7, so we sort both again.
| String | Before sort | After sort |
|---|---|---|
| s = anagram | a n a g r a m | a a a g m n r |
| t = nagaraz | n a g a r a z | a a a g n r z |
This time the compare stops early at the first mismatch. Index 4 holds m in s but n in t, so Arrays.equals returns false right there.
| Index | s sorted | t sorted | Match? |
|---|---|---|---|
| 0 | a | a | Yes |
| 1 | a | a | Yes |
| 2 | a | a | Yes |
| 3 | g | g | Yes |
| 4 | m | n | No — stop, return false |
Let us walk both traces and see exactly what sorting buys us.
The true pair: anagram vs nagaram.
Both strings pass the length check because each has 7 characters. That check is cheap and it saves us from sorting when the answer is obviously false.
Because both sorted arrays are identical, the compare passes at every index and the method returns true. Sorting worked by forcing both words into one canonical order, so any two anagrams line up perfectly.
The false pair: anagram vs nagaraz.
Here the lengths still match, so we sort both words again. The word nagaraz has a z where the true partner had an m.
At index 4 the two arrays disagree: s has m while t has n. Arrays.equals notices the mismatch and returns false immediately, without checking the rest. That early stop is why the compare is quick even when the words are long.
| 💡 Interview Insight A common opener is “what is the very first thing you check?” Say the lengths. Comparing lengths is O(1) and it kills half the false cases before you do any real work. |
Sorting is clean and short, but that log n factor is wasted effort. We are about to count letters instead, which drops the time to linear.
Skip the sorting. Walk s and add one to each letter’s count. Then walk t and subtract one for each letter. If every count lands back at zero, the words are anagrams. A negative count mid-way means t has a letter that s cannot cover, so we can stop early.
if length(s) != length(t):
return false
count = empty map from char to int
for ch in s:
count[ch] = count[ch] + 1 // add one for s
for ch in t:
count[ch] = count[ch] - 1 // remove one for t
if count[ch] < 0:
return false
return trueThink of the map as a score for each letter. The first word adds to the score, the second word takes away from it. If every score ends at zero, the words match.
import java.util.*;
public class ValidAnagramMap {
public static boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
Map<Character, Integer> count = new HashMap<>();
for (char ch : s.toCharArray()) {
count.put(ch, count.getOrDefault(ch, 0) + 1);
}
for (char ch : t.toCharArray()) {
count.put(ch, count.getOrDefault(ch, 0) - 1);
if (count.get(ch) < 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
System.out.println(isAnagram("anagram", "nagaram")); // true
System.out.println(isAnagram("anagram", "nagaraz")); // false
}
}The Java code follows the same score idea. The one helper to know is getOrDefault, which gives back 0 for a letter we have not seen yet, so we never hit a null.
True pair first, s = “anagram” and t = “nagaram”. Lengths match at 7, so we start counting. The first table shows the count-up pass over s.
| i (read index) | ch in s | map after this step |
|---|---|---|
| 0 | a | {a:1} |
| 1 | n | {a:1, n:1} |
| 2 | a | {a:2, n:1} |
| 3 | g | {a:2, n:1, g:1} |
| 4 | r | {a:2, n:1, g:1, r:1} |
| 5 | a | {a:3, n:1, g:1, r:1} |
| 6 | m | {a:3, n:1, g:1, r:1, m:1} |
Now the count-down pass over t. Each letter drops by one. Watch that no count ever goes below zero.
| j (read index) | ch in t | map after this step | Negative? |
|---|---|---|---|
| 0 | n | {a:3, n:0, g:1, r:1, m:1} | No |
| 1 | a | {a:2, n:0, g:1, r:1, m:1} | No |
| 2 | g | {a:2, n:0, g:0, r:1, m:1} | No |
| 3 | a | {a:1, n:0, g:0, r:1, m:1} | No |
| 4 | r | {a:1, n:0, g:0, r:0, m:1} | No |
| 5 | a | {a:0, n:0, g:0, r:0, m:1} | No |
| 6 | m | {a:0, n:0, g:0, r:0, m:0} | No |
Every count finished at zero and none dipped negative, so the method returns true. Now the false pair, s = “anagram” and t = “nagaraz”. The count-up pass over s is exactly the same as before, ending at {a:3, n:1, g:1, r:1, m:1}. The count-down over t is where it breaks.
| j (read index) | ch in t | map after this step | Negative? |
|---|---|---|---|
| 0 | n | {a:3, n:0, g:1, r:1, m:1} | No |
| 1 | a | {a:2, n:0, g:1, r:1, m:1} | No |
| 2 | g | {a:2, n:0, g:0, r:1, m:1} | No |
| 3 | a | {a:1, n:0, g:0, r:1, m:1} | No |
| 4 | r | {a:1, n:0, g:0, r:0, m:1} | No |
| 5 | a | {a:0, n:0, g:0, r:0, m:1} | No |
| 6 | z | {a:0, n:0, g:0, r:0, m:1, z:-1} | Yes → return false |
Let us go pass by pass and watch the map fill up and drain.
Count-up pass over s (anagram).
This pass just tallies the letters of s. The map starts empty and grows one entry at a time.
After the whole pass the map reads {a:3, n:1, g:1, r:1, m:1}. That is the exact letter fingerprint of the word anagram.
Count-down pass over t (nagaram), the true case.
Now every letter of t pulls its own count down by one. If s and t are true anagrams, each drop lands on a count that was waiting for it.
No count ever went negative, which tells us t never asked for a letter that s had run out of. Since the lengths matched too, the method safely returns true.
Count-down pass over t (nagaraz), the false case.
The first six steps are identical to the true case, because nagaraz shares its first six letters’ multiset with nagaram. The break comes at the very end.
Notice the early exit. We did not even need to finish checking or scan the map at the end. One negative count was enough proof that the words differ.
| 💡 Interview Insight If asked “why subtract instead of building a second map?”, explain that one map with add-then-subtract uses half the memory and lets you bail out the moment a count goes negative. |
This beats sorting on speed. Still, a HashMap carries boxing and hashing overhead. When the input is only lowercase a to z, we can swap the map for a tiny array and go even faster.
For lowercase English letters, a plain int array of 26 slots replaces the map. Slot 0 is a, slot 1 is b, and so on. Add one for each letter in s, subtract one for each letter in t, then check that every slot is zero. No hashing, no boxing, just fast array math. This is the answer interviewers hope to see.
if length(s) != length(t):
return false
count = array of 26 zeros
for ch in s:
count[ch - 'a'] = count[ch - 'a'] + 1
for ch in t:
count[ch - 'a'] = count[ch - 'a'] - 1
for value in count:
if value != 0:
return false
return trueThis is the same map idea, but simpler. There are only 26 lowercase letters, so we do not need a map. A plain array with one slot per letter works, and it is faster.
public class ValidAnagramCount {
public static boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] count = new int[26];
for (int i = 0; i < s.length(); i++) {
count[s.charAt(i) - 'a']++;
count[t.charAt(i) - 'a']--;
}
for (int value : count) {
if (value != 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
System.out.println(isAnagram("anagram", "nagaram")); // true
System.out.println(isAnagram("anagram", "nagaraz")); // false
}
}The code is short because the array does most of the work. The nice part is that one loop reads both strings, so we go through the input only once.
True pair first, s = “anagram” and t = “nagaram”. To keep the table readable we show only the slots that are not zero after each step. The single loop touches both strings, so each row bumps the s letter up and the t letter down in the same step.
| i | s[i] (++) | t[i] (–) | nonzero slots after step |
|---|---|---|---|
| 0 | a | n | a:1, n:-1 |
| 1 | n | a | a:0, n:0 (all zero) |
| 2 | a | g | a:1, g:-1 |
| 3 | g | a | a:0, g:0 (all zero) |
| 4 | r | r | all zero |
| 5 | a | a | all zero |
| 6 | m | m | all zero |
The final sweep finds every slot at zero, so the method returns true. Now the false pair, s = “anagram” and t = “nagaraz”. Only the last step changes, because t’s last letter is z instead of m.
| i | s[i] (++) | t[i] (–) | nonzero slots after step |
|---|---|---|---|
| 0 | a | n | a:1, n:-1 |
| 1 | n | a | all zero |
| 2 | a | g | a:1, g:-1 |
| 3 | g | a | all zero |
| 4 | r | r | all zero |
| 5 | a | a | all zero |
| 6 | m | z | m:1, z:-1 |
This time the final sweep hits slot m at 1 and slot z at -1. Both are nonzero, so the method returns false.
Let us step through the single loop and watch the 26 slots wobble around zero.
The true pair, step by step.
Each step touches two slots: the s letter goes up, the t letter goes down. Because s and t are true anagrams, these bumps keep cancelling out as we go.
The counts bounce above and below zero during the loop, which is fine. What matters is the end state. After the last step every slot sits at zero, so the final sweep confirms a true anagram.
The false pair, step by step.
The first six steps run exactly like the true case, since nagaraz matches nagaram up to that point. The difference lands on the final letter.
Now the final sweep walks all 26 slots. It finds m at 1 and z at -1, both nonzero, so it returns false. The leftover counts are the fingerprint of the mismatch: s had an extra m, and t had an extra z.
| 💡 Interview Insight Interviewers often ask “what if the input has unicode or uppercase letters?” Be honest: the 26-slot array assumes lowercase a to z. For anything wider, fall back to the HashMap, which handles any character. |
| Approach | How it decides | Extra memory | Speed feel |
|---|---|---|---|
| Sort both | Compare sorted arrays | Two char arrays | Slowest of the three |
| HashMap count | Add up, subtract down | One map of letters | Fast, linear |
| Count array | 26 slots up and down | Fixed 26 ints | Fastest and leanest |
All three return the same answer. They just pay different prices to get there.
| Approach | Time | Space | Note |
|---|---|---|---|
| Sort both strings | O(n log n) | O(n) | Simple, but sorting is wasted work |
| HashMap count | O(n) | O(k) | Fast, works for any characters |
| Count array of 26 | O(n) | O(1) | Fast and lean, the expected answer |
The two counting versions share the same linear time. What sets them apart is memory and overhead. The array of 26 uses a fixed, tiny amount of space and skips all hashing, so it edges out the map on raw speed.
In an interview, mention sorting first to show you know it, then pivot to counting. Land on the count array for lowercase input, and name the map as your fallback for wider alphabets. That progression tells a clean story.
| 💡 Interview Insight If pushed on the constant space claim, point out that the array is always 26 ints no matter how long the strings are. Size does not grow with input, so it counts as O(1) extra space. |
A few small traps catch beginners on Valid Anagram. Keep them in mind.
Run the empty-string case and a different-length case through your code before you call it done. They catch more bugs than any ordinary input will.
Valid Anagram in Java looks tiny, yet it teaches a habit you will reuse everywhere: turn a comparison into a count. Once you see two strings as bags of letters, the whole problem clicks.
Our seven-letter traces showed the payoff clearly. Sorting forced both words into one order and compared them. The map counted up then down, and the array of 26 did the same thing with almost no memory.
So take the pattern, not just the answer. When order does not matter, count instead of sort. Reach for a fixed array when the alphabet is small, and keep the map handy for anything bigger.
That counting habit turns many string and array problems from tricky into routine. It will serve you well on the harder problems waiting further down the list.