1. Overview
In this tutorial, we’ll learn various techniques to check if all the characters present in a string, including non-ASCII characters, are unique. Additionally, all the methods discussed here are case-insensitive.
2. Brute-Force
This is one of the most obvious techniques, but it’s slightly crude and may not be the most efficient. We compare the characters in the string with each other:
public class UniqueCharChecker {
public static boolean bruteForceCheck(String str) {
char[] chars = str.toUpperCase().toCharArray();
for (int i = 0; i < chars.length; i++) {
for (int j = i + 1; j < chars.length; j++) {
if(chars[i] == chars[j]) {
return false;
}
}
}
return true;
}
}
Let’s write some test cases for the above method:
public class UniqueCharCheckerUnitTest {
@Test
public void givenUnique_whenBruteForceCheck_thenReturnTrue() {
String[] sampleStrings = new String[]{"Justfewdi123", "$%&Hibusc", "Hibusc%$#", "მშვნიერ"};
final String MSG = "Duplicate found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertTrue(MSG + " in " + sampleStr, UniqueCharChecker.checkV1(sampleStr)));
}
@Test
public void givenNotUnique_whenBruteForceCheck_thenReturnFalse() {
String[] sampleStrings = new String[]{"Justfewdif123", "$%&Hibushc", "Hibusuc%$#", "Hi%busc%$#", "მშვენიერი"};
final String MSG = "Duplicate not found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertFalse(MSG + " in " + sampleStr, UniqueCharChecker.checkV1(sampleStr)));
}
}
2. Sorting
So this is similar to the brute-force technique, but here we first sort the characters in the string and then compare them with just their neighbor, and not with everyone. Let’s check out the implementation:
public static boolean sortAndThenCheck(String str) {
char[] chars = str.toUpperCase().toCharArray();
Arrays.sort(chars);
for (int i = 0; i < chars.length - 1; i++) {
if(chars[i] == chars[i+1]) {
return false;
}
}
return true;
}
Let’s test it:
@Test
public void givenUnique_whenSortAndThenCheck_thenReturnTrue() {
String[] sampleStrings = new String[]{"Justfewdi123", "$%&Hibusc", "Hibusc%$#", "მშვნიერ"};
final String MSG = "Duplicate found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertTrue(MSG + " in " + sampleStr, UniqueCharChecker.checkV2(sampleStr)));
}
@Test
public void givenNotUnique_whenSortAndThenCheck_thenReturnFalse() {
String[] sampleStrings = new String[]{"Justfewdif123", "$%&Hibushc", "Hibusuc%$#", "Hi%busc%$#", "მშვენიერი"};
final String MSG = "Duplicate not found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertFalse(MSG + " in " + sampleStr, UniqueCharChecker.checkV2(sampleStr)));
}
3. HashSet
Here, we leverage the power of java.util.Set to remove duplicate characters:
public static boolean useSetCheck(String str) {
char[] chars = str.toUpperCase().toCharArray();
Set <Character> set = new HashSet <>();
for (char c: chars) {
if (!set.add(c)) {
return false;
}
}
return true;
}
Now, let’s have a look at the test cases:
@Test
public void givenUnique_whenUseSetCheck_thenReturnTrue() {
String[] sampleStrings = new String[]{"Justfewdi123", "$%&Hibusc", "Hibusc%$#", "მშვნიერ" };
final String MSG = "Duplicate found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertTrue(MSG + " in " + sampleStr, UniqueCharChecker.checkV3(sampleStr)));
}
@Test
public void givenNotUnique_whenUseSetCheck_thenReturnFalse() {
String[] sampleStrings = new String[]{"Justfewdif123", "$%&Hibushc", "Hibusuc%$#", "Hi%busc%$#", "მშვენიერი"};
final String MSG = "Duplicate not found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertFalse(MSG + " in " + sampleStr, UniqueCharChecker.checkV3(sampleStr)));
}
4. Java Streams
This technique is similar to the method used in the previous section. However, we’re using the Streams API to create the Set. Let’s have a look at the implementation:
public static boolean useStreamCheck(String str) {
boolean isUnique = str.toUpperCase().chars()
.mapToObj(c -> (char) c)
.collect(Collectors.toSet())
.size() == str.length();
return isUnique;
}
And let’s check the unit tests:
@Test
public void givenUnique_whenUseStreamCheck_thenReturnTrue() {
String[] sampleStrings = new String[]{"Justfewdi123", "$%&Hibusc", "Hibusc%$#", "მშვნიერ" };
final String MSG = "Duplicate found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertTrue(MSG + " in " + sampleStr, UniqueCharChecker.checkV1(sampleStr)));
}
@Test
public void givenNotUnique_whenUseStreamCheck_thenReturnFalse() {
String[] sampleStrings = new String[]{"Justfewdif123", "$%&Hibushc", "Hibusuc%$#", "Hi%busc%$#", "მშვენიერი"};
final String MSG = "Duplicate not found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertFalse(MSG + " in " + sampleStr, UniqueCharChecker.checkV4(sampleStr)));
}
5. StringUtils
Basically, here, we’ll use the method containsIgnoreCase() in the commons-lang StringUtils class:
public static boolean useStringUtilscheck(String str) {
for (int i = 0; i < str.length(); i++) {
String curChar = String.valueOf(str.charAt(i));
String remainingStr = str.substring(i+1);
if(StringUtils.containsIgnoreCase(remainingStr, curChar)) {
return false;
}
}
return true;
}
Let’s test this method:
@Test
public void givenUnique_whenUseStringUtilscheck_thenReturnTrue() {
String[] sampleStrings = new String[]{"Justfewdi123", "$%&Hibusc", "Hibusc%$#", "მშვნიერ"};
final String MSG = "Duplicate found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertTrue(MSG + " in " + sampleStr, UniqueCharChecker.checkV5(sampleStr)));
}
@Test
public void givenNotUnique_whenUseStringUtilscheck_thenReturnFalse() {
String[] sampleStrings = new String[]{"Justfewdif123", "$%&Hibushc", "Hibusuc%$#", "Hi%busc%$#", "მშვენიერი"};
final String MSG = "Duplicate not found";
Arrays.stream(sampleStrings)
.forEach(sampleStr -> assertFalse(MSG + " in " + sampleStr, UniqueCharChecker.checkV5(sampleStr)));
}
6. Conclusion
In this tutorial, we saw five different ways to check if a string has unique characters. We also conclude that there are no out-of-the-box libraries available for solving this problem.
The code snippets used here, along with associated JUnit test cases, are available over on GitHub.