Home > database >  How to cast lambda parameter to char in the ifPresentOrElse() method
How to cast lambda parameter to char in the ifPresentOrElse() method

Time:06-13

How do I fix this code block (in ifPresentOrElse())?

I'm stuck here with:

Inconvertible types; cannot cast '<lambda parameter>' to 'char'

Please advise how to get this compiled and running.

public static boolean isBracketsInOrder1(String bracket) {
    
    Stack<Character> charStack = new Stack<>();
    static Map<Character, Character> leftToRightBracketsMap = 
                                       Map.of('{', '}', '[', ']', '(', ')');
    bracket.chars()
        .filter(i -> leftToRightBracketsMap.containsKey((char) i))
        .findFirst()
        .ifPresentOrElse((i) -> charStack.push((char) i),
            (i) -> {
                // code does not COMPILE at equals((char) i)
                return leftToRightBracketsMap.get(charStack.pop()).equals((char) i);
            });
    return true;
}

And this is the working code using for loop representing what I'm trying to implement above using streams above.

public static boolean isBracketsInOrder(String bracket) {
    Stack<Character> charStack = new Stack<>();
    static Map<Character, Character> leftToRightBracketsMap = 
                                       Map.of('{', '}', '[', ']', '(', ')');
    boolean matchFound = true;
    for (char c : bracket.toCharArray()) {
        if (leftToRightBracketsMap.containsKey(c)) charStack.push(c);
        else {
            char leftBrack = charStack.pop();
            char correctBrack = leftToRightBracketsMap.get(leftBrack);
            if (c != correctBrack) return false;
        }
    }
    return true;
}

CodePudding user response:

You've introduced the code for a very basic algorithmic question - validate a string of brackets.

There are many mistakes in your code:

  • A stream doesn't act precisely like a loop, when it hits the terminal operation (which is findFirst() in your code), it's done. Code inside ifPresentOrElse() would not be executed multiple times (you probably expected the opposite). It would be invoked only once on an optional result returned by the findFirst().

  • As its second argument ifPresentOrElse() expects an instance of the Runnable interface. Method run() neither expects any arguments, no returns a value. Therefore, this attempt to define a Runnable is incorrect: (i) -> { return something; }.

  • Any lambda expressions should to conform to a particular functional interface (see). It can't appear out of nowhere.

  • Class Stack is legacy, it's still in the JDK for backward compatibility reasons. Implementations of the Deque interface should be used instead.

  • You are not checking whether the stack is empty, which can cause an EmptyStackException. If you would replace the Stack with ArrayDeque the problem will remain, method pop() will throw NoSuchElementException. You need to make sure that stack is not empty.

  • Returning true in the imperative solution is not correct. Instead, you need to return charStack.isEmpty(), because if there are some elements in the stack - sequence is not valid, there are brackets that haven't been closed.

Implementing this problem using streams requires far more efforts than a solution using a plain loop.

According to the documentation, the only place where mutation can occur in a stream is inside the collector. As a general rule, all functions used in a stream pipeline should not operate via side effects and accumulate the stated outside the stream (apart from some edge cases, see the link). Only collector's mutable container should maintain a state.

We can contract such a collector using Collecor.of() method:

public static boolean isValidBracketSequence(String brackets) {
    return brackets.chars()
        .mapToObj(c -> (char) c)
        .collect(getBracketCollector());
}

public static Collector<Character, ?, Boolean> getBracketCollector() {
    
    return Collector.of(
        BracketContainer::new,
        BracketContainer::add,
        (left, right) -> { throw new AssertionError("should not be executed in parallel"); },
        bracketContainer -> bracketContainer.isValid() && bracketContainer.isEmpty()
    );
}

That's how a mutable container might look like:

class BracketContainer {
    public static final Map<Character, Character> leftToRightBracketsMap =
        Map.of('(', ')', '[', ']', '{', '}');
    
    private Deque<Character> stack = new ArrayDeque<>();
    private boolean isValid = true;
    
    public void add(Character next) {
        if (!isValid) return;
        
        if (leftToRightBracketsMap.containsKey(next)) {
            stack.push(next);
        } else {
            compare(next);
        }
    }
    
    public void compare(Character next) {
        
        this.isValid = !isEmpty() && leftToRightBracketsMap.get(stack.pop()).equals(next);
    }
    
    public boolean isEmpty() {
        return stack.isEmpty();
    }
    
    public boolean isValid() {
        return isValid;
    }
}

main()

public static void main(String[] args) {
    System.out.println("(([])) -> "   isValidBracketSequence("(([]))")); // true
    System.out.println("(([]]) -> "   isValidBracketSequence("(([]])")); // false
    System.out.println("(([})) -> "   isValidBracketSequence("(([}))")); // false
    System.out.println("({[])) -> "   isValidBracketSequence("({[]))")); // false
    System.out.println("({[]}) -> "   isValidBracketSequence("({[]})")); // true
}

Output:

(([])) -> true
(([]]) -> false
(([})) -> false
({[])) -> false
({[]}) -> true

A link to Online Demo

  • Related