Search code examples
javaalgorithmjava-streamunique

Getting intermediate results from stream to be used later in stream


I was trying to write some functional programming code (using lambdas and streams from Java 8) to test if a string has unique characters in it (if it does, return true, if it does not, return false). A common way to do this using vanilla Java is with a data structure like a set, i.e.:

public static boolean oldSchoolMethod(String str) {
    Set<String> set = new HashSet<>();

    for(int i=0; i<str.length(); i++) {
        if(!set.add(str.charAt(i) + "")) return false;
    }
    return true;
}

The set returns true if the character/object can be added to the set (because it did not exist there previously). It returns false if it cannot (it exists in the set already, duplicated value, and cannot be added). This makes it easy to break out the loop and detect if you have a duplicate, without needing to iterate through all length N characters of the string.

I know in Java 8 streams you cannot break out a stream. Is there anyway way to capture the return value of an intermediate stream operation, like adding to the set's return value (true or false) and send that value to the next stage of the pipeline (another intermediate operation or terminal stream operation)? i.e.

Arrays.stream(myInputString.split(""))
    .forEach( i -> {
       set.add(i) // need to capture whether this returns "true" or "false" and use that value later in 
                  // the pipeline or is this bad/not possible?
    });

One of the other ways I thought of solving this problem, is to just use distinct() and collect the results into a new string and if it is the same length as the original string, than you know it is unique, else if there are different lengths, some characters got filtered out for not being distinct, thus you know it is not unique when comparing lengths. The only issue I see here is that you have to iterate through all length N chars of the string, where the "old school" method best-case scenario could be done in almost constant time O(1), since it is breaking out the loop and returning as soon as it finds 1 duplicated character:

public static boolean java8StreamMethod(String str) {
    String result = Arrays.stream(str.split(""))
            .distinct()
            .collect(Collectors.joining());

    return result.length() == str.length();
}

Solution

  • This code might work for you:

    public class Test {
    
        public static void main(String[] args) {
    
            String myInputString = "hellowrd";
    
            HashSet<String> set = new HashSet<>();
            Optional<String> duplicateChar =Arrays.stream(myInputString.split("")).
                    filter(num-> !set.add(num)).findFirst();
            if(duplicateChar.isPresent()){
                System.out.println("Not unique");
            }else{
                System.out.println("Unique");
            }
    
        }
    }
    

    Here using findFirst() I am able to find the first duplicate element. So that we don't need to continue on iterating rest of the characters.