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 insideifPresentOrElse()
would not be executed multiple times (you probably expected the opposite). It would be invoked only once on an optional result returned by thefindFirst()
.As its second argument
ifPresentOrElse()
expects an instance of theRunnable
interface. Methodrun()
neither expects any arguments, no returns a value. Therefore, this attempt to define aRunnable
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 theDeque
interface should be used instead.You are not checking whether the stack is empty, which can cause an
EmptyStackException
. If you would replace theStack
withArrayDeque
the problem will remain, methodpop()
will throwNoSuchElementException
. You need to make sure that stack is not empty.Returning
true
in the imperative solution is not correct. Instead, you need to returncharStack.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