Soundex is a phonetic algorithm that encodes a name as one letter followed by three digits, so that names which sound alike in English, such as Robert and Rupert, get the same code, R163. Comparing the codes instead of the spellings lets us find a name even when it was typed or recorded with small spelling differences.
Soundex powers phonetic search in Java, which means searching people by name in a CRM, a hospital register or a genealogy database when the exact spelling is uncertain. Databases such as MySQL, Oracle and SQL Server have a built-in SOUNDEX() function for the same purpose.
The following example encodes a few names with the soundex() method from section 2.
String robert = soundex("Robert"); // "R163"
String rupert = soundex("Rupert"); // "R163"
String ashcraft = soundex("Ashcraft"); // "A261"
String lee = soundex("Lee"); // "L000"
boolean sameSound = soundex("Smith").equals(soundex("Smyth")); // true
Short names are padded with zeros, and long names are cut after three digits. The rules come first with a trace, followed by a correct Java implementation, the Apache Commons Codec alternative, and a comparison with other phonetic and fuzzy matching algorithms.
1. How the Soundex Algorithm Works
Soundex keeps the first letter of the name and turns the following consonants into digits by sound. Letters that are pronounced in a similar way share a digit, and vowels are dropped, because they vary most between spellings.
| Letters | Digit |
|---|---|
| B, F, P, V | 1 |
| C, G, J, K, Q, S, X, Z | 2 |
| D, T | 3 |
| L | 4 |
| M, N | 5 |
| R | 6 |
| A, E, I, O, U, Y | dropped, but they separate equal digits |
| H, W | dropped, and they do not separate equal digits |
The full code follows four rules, in this order.
- Keep the first letter of the name in uppercase.
- Replace each following consonant with its digit, and skip H and W completely.
- Write a digit only when it differs from the digit of the previous letter, including the first letter. A vowel resets that comparison, so equal digits on both sides of a vowel are both written.
- Stop after three digits, and pad with zeros when the name has fewer than three.
The H and W rule is the part most implementations get wrong. In Ashcraft, the letters S and C both map to 2, and the H between them does not separate them, so only the first 2 is written.

2. Soundex Implementation in Java
The implementation cleans the input first, because real names contain apostrophes, hyphens, spaces and lowercase letters. It keeps the letters A to Z and handles null and empty input without exceptions. The class and its tests are in the Core-Java repository (Java 25).
static String soundex(String name) {
if (name == null) {
return null;
}
StringBuilder letters = new StringBuilder();
for (char c : name.toUpperCase(Locale.ROOT).toCharArray()) {
if (c >= 'A' && c <= 'Z') {
letters.append(c); // drop spaces, ' and -
}
}
if (letters.isEmpty()) {
return "";
}
StringBuilder code = new StringBuilder().append(letters.charAt(0));
char last = digit(letters.charAt(0));
for (int i = 1; i < letters.length() && code.length() < 4; i++) {
char c = letters.charAt(i);
if (c == 'H' || c == 'W') {
continue; // no separator
}
char d = digit(c);
if (d != '0' && d != last) {
code.append(d);
}
last = d; // a vowel sets last to '0'
}
while (code.length() < 4) {
code.append('0');
}
return code.toString();
}
static char digit(char c) {
return switch (c) {
case 'B', 'F', 'P', 'V' -> '1';
case 'C', 'G', 'J', 'K', 'Q', 'S', 'X', 'Z' -> '2';
case 'D', 'T' -> '3';
case 'L' -> '4';
case 'M', 'N' -> '5';
case 'R' -> '6';
default -> '0'; // vowels
};
}
String pfister = soundex("Pfister"); // "P236"
String tymczak = soundex("Tymczak"); // "T522"
String obrien = soundex("O'Brien"); // "O165"
String blank = soundex(""); // ""
String none = soundex(null); // null
In Pfister, the P and F both map to 1, so the F is not written. In Tymczak, the two 2 digits of C and Z are separated by the vowel A, so both are written, which gives T522.
3. Soundex with Apache Commons Codec
For production code, the Soundex class from Apache Commons Codec is the safer choice, because it is widely tested and offers variants such as US_ENGLISH_SIMPLIFIED and US_ENGLISH_GENEALOGY. We add the dependency.
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.22.1</version>
</dependency>
Soundex sx = new Soundex();
String code = sx.soundex("Ashcraft"); // "A261"
int similarity = sx.difference("Robert", "Rupert"); // 4
int different = sx.difference("Smith", "Jones"); // 2
String simplified = Soundex.US_ENGLISH_SIMPLIFIED.soundex("Ashcraft"); // "A226"
The difference() method compares two codes and returns the number of matching characters, from 0 to 4, where 4 means the names sound alike. The US_ENGLISH_SIMPLIFIED variant ignores the H and W rule, which is why it returns A226 for Ashcraft, the code that many older implementations produce by mistake.
Our implementation and Commons Codec returned the same codes for all 22 test names we checked, including Ashcroft, Gauss and Ghosh (G200), Hilbert and Heilbronn (H416), Lloyd (L300) and Moses (M220).
One difference matters for real data. Commons Codec throws IllegalArgumentException for letters outside A to Z, such as the ü in Müller, whereas our version drops them, which can change the code when the dropped letter is a consonant. A cleaner fix is to strip the accents before encoding, so the base letter stays.
String plain = Normalizer.normalize("M\u00fcller", Normalizer.Form.NFD).replaceAll("\\p{M}", ""); // "Muller"
String muller = new Soundex().soundex(plain); // "M460"
4. Phonetic Search Example
Take a clinic’s patient search. The receptionist types Jonson, and the database contains Johnson, Johnston and Jansen. An exact search finds nothing, but a search on Soundex codes finds the likely matches.
List<String> patients = List.of("Johnson", "Johnston", "Jansen", "Jackson", "Peters");
String typed = "Jonson";
List<String> matches = patients.stream()
.filter(p -> soundex(p).equals(soundex(typed)))
.toList(); // [Johnson, Jansen]
Johnston gets J523 because of its T, so it is missing from the result, which shows a limit of Soundex. In a database, we store the code in an indexed column when the row is saved, so the search is an index lookup instead of encoding every row on each query.
5. Soundex vs Other Phonetic and Fuzzy Matching Algorithms
Soundex is old and simple, so it matches some unrelated names and misses some related ones. Commons Codec contains several improved phonetic encoders, and edit distance is the other common approach.
| Algorithm | Output for Robert | Strength | Java class |
|---|---|---|---|
| Soundex | R163 | Simple, built into most databases | Soundex (Commons Codec) |
| Refined Soundex | R901096 | More digit groups, fewer false matches | RefinedSoundex |
| Metaphone | RBRT | Uses English pronunciation rules | Metaphone |
| Double Metaphone | RPRT | Two codes for names with two common pronunciations | DoubleMetaphone |
| NYSIIS | RABAD | Designed for names in New York State records | Nysiis |
| Cologne phonetics | 657 for Müller | Designed for German names | ColognePhonetic |
| Levenshtein distance | Number of edits | Catches typos in any word, not only names | LevenshteinDistance (Commons Text) |
For English names, Double Metaphone applies many more pronunciation rules than Soundex, such as silent letters and PH sounding like F. For typos in product names or search queries, edit distance fits better, because phonetic codes ignore letters that sound alike but change the meaning.
6. Soundex FAQs
History, length and language support are the common Soundex questions.
6.1. Who invented Soundex?
Robert C. Russell and Margaret King Odell patented Soundex in the United States in 1918 and 1922. The US National Archives used it to index the census records from 1880 onward, which is why genealogy sites still use it.
6.2. Why is the Soundex code always four characters?
The fixed length makes the codes simple to index and compare. One letter plus three digits is enough to group similar names, and padding with zeros keeps short names such as Lee (L000) in the same format.
6.3. Does Soundex work for non-English names?
Poorly. The digit groups follow English pronunciation, so many names from other languages get misleading codes. Cologne phonetics for German, or Beider-Morse and Daitch-Mokotoff Soundex for Eastern European names, give better results, and Commons Codec includes them.
6.4. Is the MySQL SOUNDEX() function the same?
Not quite. MySQL returns codes longer than four characters for long names, so the result differs from the standard algorithm. To compare codes, we use LEFT(SOUNDEX(name), 4) or compute the code in Java.
7. Conclusion
Soundex turns a name into one letter and three digits by keeping the first letter, mapping consonants to sound groups and dropping vowels, H and W. The H and W rule decides whether names like Ashcraft encode correctly, so it is worth testing with the standard examples.
For production code, Apache Commons Codec offers a tested Soundex class with difference() and variants. When matching quality matters more than simplicity, Double Metaphone or edit distance find better matches.
8. References
- Apache Commons Codec, Soundex Javadoc
- US National Archives, The Soundex Indexing System
- commons-codec on Maven Central
Happy Learning !!