Skip to content

Instantly share code, notes, and snippets.

@matey-jack
Last active June 29, 2017 22:34
Show Gist options
  • Save matey-jack/403f551be3feda568893fc6a76dca45b to your computer and use it in GitHub Desktop.
Save matey-jack/403f551be3feda568893fc6a76dca45b to your computer and use it in GitHub Desktop.
Nice Anagram Detector Example in Java 8, with examples in JUnit
import java.util.HashMap;
import java.util.Map;
public class AnagramDetector {
public Map<Character, Integer> countLetters(String s) {
Map<Character, Integer> result = new HashMap<>();
for (char c : s.toLowerCase().toCharArray()) {
result.put(c, result.getOrDefault(c, 0) + 1);
// alternative, not reading as nicely, but using one less Map lookup:
// result.compute(c, (ignored, oldCount) -> oldCount == null ? 1 : oldCount+1);
}
return result;
}
public boolean isAnagramOf(String a, String b) {
if (a == null || b == null) {
throw new IllegalArgumentException("isAnagramOf: arguments must be non-null.");
}
if (a.equals(b)) {
return true;
}
return countLetters(a).equals(countLetters(b));
}
}
import java.util.Arrays;
public class AnagramDetectorViaSort {
// I wish the library function would already do it like this, but it was made before Java embraced functional programming!
public char[] sort(char[] chars) {
Arrays.sort(chars);
return chars;
}
public char[] sortedLowerCaseCharsOf(String s) {
return sort(s.toLowerCase().toCharArray());
}
public boolean isAnagramOf(String a, String b) {
if (a == null || b == null) {
throw new IllegalArgumentException("isAnagramOf: arguments must be non-null.");
}
if (a.equals(b)) {
return true;
}
return Arrays.equals(sortedLowerCaseCharsOf(a), sortedLowerCaseCharsOf(b));
}
}
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class AnagramTest {
AnagramDetector underTest = new AnagramDetector();
@Test
public void testWithIdentity() {
assertTrue(underTest.isAnagramOf("post", "post"));
}
@Test
public void testWithSimpleAnagram() {
assertTrue(underTest.isAnagramOf("post", "stop"));
}
@Test
public void testWithAnagram_DuplicateLetter() {
assertTrue(underTest.isAnagramOf("Kammer", "Kramem"));
}
@Test
public void testWithNoAnagram_Simple() {
assertFalse(underTest.isAnagramOf("post", "plus"));
}
@Test
public void testWithNoAnagram_DuplicateLetters() {
assertFalse(underTest.isAnagramOf("post", "stopp"));
}
@Test
public void testWithNoAnagram_SeveralDuplicateLetters() {
assertFalse(underTest.isAnagramOf("flussschifffahrt", "iffussahrtsch"));
}
}
@matey-jack
Copy link
Author

matey-jack commented Jun 29, 2017

I did this as a coding exercise and implemented my first solution idea with the Map, because it was good enough and I wanted to get it done.

Later search revealed a solution using less code (sorting the char arrays and comparing them) as well as a solution that is a bit faster (using int arrays indexed by the character code as counters instead of the Map). Obviously the faster solution requires a known and fixed set of characters (such as the English alphabet which is very boring and restricted). Using just the 26 Latin characters would already fail for my native language and just as well for a lot of other languages, which makes me think that my solution is not so bad after all.

Although the sorting idea is really nice, so I also added it for comparison.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment