One more solution, based on occurrence counter:
static boolean areAnagrams(CharSequence a, CharSequence b) {
int len = a.length();
if (len != b.length())
return false;
// collect char occurrences in "a"
Map<Character, Integer> occurrences = new HashMap<>(64);
for (int i = 0; i < len; i++)
occurrences.merge(a.charAt(i), 1, Integer::sum);
// for each char in "b", look for matching occurrence
for (int i = 0; i < len; i++) {
char c = b.charAt(i);
int cc = occurrences.getOrDefault(c, 0);
if (cc == 0)
return false;
occurrences.put(c, cc - 1);
}
return true;
}
Though this solution is less elegant than "sort-and-compare", it might be more effective for long strings with a small chances to be anagrams since it operates in O(n) instead of O(n logn) and returns as soon as matching occurrence is not found at some position in a second string.
Stepping out of "Basic Java" territory, I modified the algorithm to handle surrogate pairs as well. Here collected and matched are not char
s, but int
codepoints:
static boolean areAnagrams(CharSequence a, CharSequence b) {
int len = a.length();
if (len != b.length())
return false;
// collect codepoint occurrences in "a"
Map<Integer, Integer> ocr = new HashMap<>(64);
a.codePoints().forEach(c -> ocr.merge(c, 1, Integer::sum));
// for each codepoint in "b", look for matching occurrence
for (int i = 0, c = 0; i < len; i += Character.charCount(c)) {
int cc = ocr.getOrDefault((c = Character.codePointAt(b, i)), 0);
if (cc == 0)
return false;
ocr.put(c, cc - 1);
}
return true;
}