Core Java

Java Unique String Check

Hello. In this tutorial, we will understand different implementations in Java to check unique characters in a string.

1. Introduction

  • Brute-Force: Brute-force is a straightforward algorithmic approach where all possible solutions to a problem are systematically tried to find the correct one. In the context of checking if a string has all unique characters, a brute-force method involves comparing each character in the string to every other character to detect duplicates. While simple, this approach can be inefficient for larger inputs.
  • Sorting: Sorting is the process of arranging elements in a specific order, often ascending or descending. When applied to strings, sorting can help identify duplicate characters adjacent to each other, making it easier to check for uniqueness. However, sorting has a time complexity that might not be optimal for large strings or frequent checks.
  • HashSet: HashSet is a Java collection that stores a collection of unique elements. In the context of checking for unique characters in a string, a HashSet can be used to keep track of characters encountered so far. Adding characters to a HashSet and checking for duplicates using this data structure is more efficient than brute-force approaches.
  • Java Streams: Java Streams are a feature introduced in Java 8 that provides a way to process collections of data in a functional and declarative manner. Streams can be used to check for unique characters in a string by converting the string to a stream of characters and then using distinct operations to ensure uniqueness.
  • StringUtils: StringUtils is a utility class in Java, often provided by libraries like Apache Commons, that offers a range of methods for performing various operations on strings. When checking for unique characters, StringUtils might provide helpful methods for transforming and comparing strings, potentially streamlining the process.

1.1 Contrast among various implementations

TopicAdvantagesDisadvantages
Brute-Force
  • Simple to understand and implement.
  • Suitable for small problem sizes.
  • Inefficient for large problem sizes due to exhaustive search.
  • May not provide optimal solutions.
Sorting
  • Adjacent duplicate characters can be easily identified after sorting.
  • Can be effective for certain problem sizes and use cases.
  • Sorting has a time complexity that might not be optimal for large strings.
  • Overkill for simple uniqueness checks.
HashSet
  • Efficient for checking and tracking unique elements.
  • Constant-time performance for basic operations.
  • Requires additional memory for storing the hash set.
  • May have some overhead for hashing and handling collisions.
Java Streams
  • Functional and declarative approach for data processing.
  • This can lead to more readable and concise code.
  • May introduce some learning curve for those new to streams.
  • May not be the most efficient solution for all scenarios.
StringUtils
  • Provides a collection of helpful string manipulation methods.
  • Can streamline string operations and improve code readability.
  • Dependent on external libraries (e.g., Apache Commons).
  • This may introduce additional dependencies to the project.

2. Brute-Force

The brute-force approach involves comparing each character in the string with every other character to check for duplicates.

In this example, the hasAllUniqueCharacters function checks if a given string contains all unique characters. It does this by comparing each character with every other character in the string. If it finds any two characters that are the same, it immediately returns false, indicating that the string does not have all unique characters.

BruteForceUniqueCharChecker.java

package com.jcg.example;

public class BruteForceUniqueCharChecker {

    public static boolean hasAllUniqueCharacters(String str) {
        int length = str.length();
        for (int i = 0; i < length - 1; i++) {
            for (int j = i + 1; j < length; j++) {
                if (str.charAt(i) == str.charAt(j)) {
                    return false; // Found a duplicate character
                }
            }
        }
        return true; // All characters are unique
    }

    public static void main(String[] args) {
        String testStr1 = "abcdef";
        String testStr2 = "hello";

        System.out.println(testStr1 + " has all unique characters: " + hasAllUniqueCharacters(testStr1));
        System.out.println(testStr2 + " has all unique characters: " + hasAllUniqueCharacters(testStr2));
    }
}

Keep in mind that the brute-force approach here has a time complexity of O(n^2), where n is the length of the string. This means that for longer strings, the algorithm’s performance can degrade quickly. For more efficient solutions, you could use data structures like a HashSet or an array of boolean flags to keep track of characters encountered. These optimized solutions would have a linear time complexity of O(n), providing better performance for larger strings.

2.1 Output

Run the code in the IDE of your choice and it should print the following output on the console.

Fig. 1: Brute-Force example output

3. Sorting

The sorting approach involves sorting the characters in the string and then checking for adjacent duplicate characters. If any adjacent characters are the same, the string does not have all unique characters.

In this example, the hasAllUniqueCharacters function sorts the characters in the given string using the Arrays.sort method. Then, it iterates through the sorted characters and checks for adjacent duplicates. If any adjacent characters are the same, the function returns false, indicating that the string does not have all unique characters.

SortingUniqueCharChecker.java

package com.jcg.example;

import java.util.Arrays;

public class SortingUniqueCharChecker {

    public static boolean hasAllUniqueCharacters(String str) {
        char[] charArray = str.toCharArray();
        Arrays.sort(charArray); // Sort the characters

        for (int i = 0; i < charArray.length - 1; i++) {
            if (charArray[i] == charArray[i + 1]) {
                return false; // Found adjacent duplicate characters
            }
        }
        return true; // No adjacent duplicates found
    }

    public static void main(String[] args) {
        String testStr1 = "abcdef";
        String testStr2 = "hello";

        System.out.println(testStr1 + " has all unique characters: " + hasAllUniqueCharacters(testStr1));
        System.out.println(testStr2 + " has all unique characters: " + hasAllUniqueCharacters(testStr2));
    }
}

It’s important to note that while the sorting approach is a viable option, it might not be the most efficient solution for all scenarios, especially when compared to alternatives like using a HashSet or optimized data structures.

3.1 Output

Run the code in the IDE of your choice and it should print the following output on the console.

Fig. 2: Sorting example output

4. HashSet

The HashSet approach involves using a HashSet data structure to keep track of characters encountered while iterating through the string. If a character is already in the HashSet, it means the string does not have all unique characters.

In this example, the hasAllUniqueCharacters function iterates through each character in the given string. For each character, it checks if the HashSet already contains the character. If it does, the function returns false since a duplicate character is found. If the character is not in the HashSet, it is added to the set. If the loop completes without finding any duplicates, the function returns true.

HashSetUniqueCharChecker.java

package com.jcg.example;

import java.util.HashSet;

public class HashSetUniqueCharChecker {

    public static boolean hasAllUniqueCharacters(String str) {
        HashSet<Character> charSet = new HashSet<>();

        for (char c : str.toCharArray()) {
            if (charSet.contains(c)) {
                return false; // Found a duplicate character
            }
            charSet.add(c); // Add character to the set
        }
        return true; // No duplicates found
    }

    public static void main(String[] args) {
        String testStr1 = "abcdef";
        String testStr2 = "hello";

        System.out.println(testStr1 + " has all unique characters: " + hasAllUniqueCharacters(testStr1));
        System.out.println(testStr2 + " has all unique characters: " + hasAllUniqueCharacters(testStr2));
    }
}

The HashSet approach is a practical solution for checking the uniqueness of characters in a string. It has better performance than the brute-force approach and is often preferred for larger strings or more complex problems.

4.1 Output

Run the code in the IDE of your choice and it should print the following output on the console.

Fig. 3: HashSet example output

5. Java Streams

Java Streams are a feature introduced in Java 8 that provides a more functional and declarative way to process collections of data. They allow operations to be performed on the data in a pipeline-like manner, making the code more concise and readable. While Java Streams might not be the most optimized solution for this specific problem, they offer an interesting approach to solving it.

In this example, the hasAllUniqueCharacters function uses Java Streams to process the characters in the given string. It uses the chars() method to convert the string to an IntStream of Unicode values. Then, it applies the distinct() operation to remove duplicate characters and counts the remaining distinct characters using the count() operation. If the count of distinct characters is equal to the length of the original string, it means all characters are unique.

StreamUniqueCharChecker.java

package com.jcg.example;

public class StreamUniqueCharChecker {

    public static boolean hasAllUniqueCharacters(String str) {
        long distinctCount = str.chars().distinct().count();
        return distinctCount == str.length();
    }

    public static void main(String[] args) {
        String testStr1 = "abcdef";
        String testStr2 = "hello";

        System.out.println(testStr1 + " has all unique characters: " + hasAllUniqueCharacters(testStr1));
        System.out.println(testStr2 + " has all unique characters: " + hasAllUniqueCharacters(testStr2));
    }
}

Java Streams offer an elegant way to express data processing logic, but they might not be the best fit for all problem scenarios. It’s important to consider the trade-offs between readability, maintainability, and performance when choosing an approach.

5.1 Output

Run the code in the IDE of your choice and it should print the following output on the console.

Fig. 4: Java Streams example output

6. StringUtils

StringUtils is a utility class provided by libraries like Apache Commons lang. It offers a collection of methods for performing various operations on strings, making string manipulation tasks more convenient. While StringUtils itself might not have a method specifically tailored for checking unique characters, its methods can be used to streamline the process. Do note that StringUtils itself does not have a method specifically for checking unique characters in a string.

StringUtilsUniqueCharChecker.java

package com.jcg.example;

import org.apache.commons.lang3.StringUtils;

public class StringUtilsUniqueCharChecker {

    public static boolean hasAllUniqueCharacters(String str) {
        String sortedStr = StringUtils.sort(str);
        return sortedStr.equals(str);
    }

    public static void main(String[] args) {
        String testStr1 = "abcdef";
        String testStr2 = "hello";

        System.out.println(testStr1 + " has all unique characters: " + hasAllUniqueCharacters(testStr1));
        System.out.println(testStr2 + " has all unique characters: " + hasAllUniqueCharacters(testStr2));
    }
}

While StringUtils can be helpful for general string manipulation, it’s important to ensure that the library’s features are used in a way that is appropriate and efficient for the specific problem at hand. In this case, using optimized data structures like HashSet might be more suitable for checking the uniqueness of characters in a string.

7. Conclusion

In conclusion, we have explored several techniques for checking if a string contains all unique characters. Each technique offers its advantages and disadvantages, making them suitable for different problem sizes, complexities, and code readability concerns.

  • Brute-Force Approach: The brute-force approach involves comparing each character in the string with every other character, which can be simple to understand and implement. However, it becomes inefficient for larger problem sizes due to its exhaustive search nature and may not provide optimal solutions.
  • Sorting Approach: Sorting the characters in the string and checking for adjacent duplicates is effective for certain problem sizes. While it efficiently detects adjacent duplicate characters, sorting’s time complexity might not be optimal for larger strings. It can be overkill for simple uniqueness checks.
  • HashSet Approach: Utilizing a HashSet data structure offers efficiency in tracking and checking unique elements. It provides constant-time performance for basic operations and is effective for larger problem sizes. However, it requires additional memory for storing the hash set and may introduce some hashing and collision-handling overhead.
  • Java Streams Approach: Java Streams provide a functional and declarative way to process data, improving code readability and maintainability. However, they might not be the most efficient solution for all scenarios, and there could be a learning curve for those new to streams.
  • StringUtils Approach: The StringUtils utility class streamlines various string manipulation tasks, potentially enhancing code readability. However, it might not be specifically designed for solving the unique characters problem and could introduce external library dependencies.

In choosing a technique, it’s essential to consider the problem’s constraints, input sizes, and other factors such as efficiency, readability, and maintainability of the code. While each approach has its strengths, the suitability of the technique largely depends on the context of the problem at hand.

8. Download the Files

This was a tutorial to understand different implementations in Java to check unique characters in a string.

Download
You can download the files of this example here: Java Unique String Check

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button