Java 8中如何用Streams和Lambda更优雅实现带if-else的方法?
Great question! Your current Java 8 attempt uses forEach but misses critical logic (like checking for an empty stack before popping, and the ability to early-exit when invalid) — plus forEach doesn’t play nicely with early termination, which is why it feels almost identical to the Java 7 loop. Let’s fix that and make it more idiomatic with Java 8 stream features.
First, the Problem with Your Current Java 8 Code
Your version doesn’t handle cases where we encounter a closing bracket with an empty stack, and it can’t return false early (since forEach can’t interrupt the stream). This would throw a NoSuchElementException if you pass an invalid string like ")", which the Java 7 code correctly handles by returning false.
A Clean, Efficient Java 8 Solution
Instead of forEach, use allMatch — this method short-circuits (stops processing as soon as it hits a false), which mimics the early return behavior of your original loop. Here’s how to implement it:
private static boolean execute(String expression) { Deque<Character> stack = new ArrayDeque<>(); // Use chars() to get an IntStream of character codes, then map to char boolean allValid = expression.chars() .mapToObj(c -> (char) c) .allMatch(c -> { if (isOpenToken(c)) { stack.push(c); return true; // Keep processing further characters } // Check if stack is empty or brackets don't match if (stack.isEmpty() || !matches(stack.pop(), c)) { return false; // Short-circuit: invalid, stop processing immediately } return true; // Valid so far, continue }); // After all characters are processed, stack must be empty to be valid return allValid && stack.isEmpty(); }
Why This Works Better
- Short-circuiting:
allMatchstops processing immediately when it encounters an invalid character, just like your originalreturn falsein the Java 7 loop. - Cleaner stream pipeline: We use
expression.chars()directly instead of converting to achar[]first, which is more concise and reads better. - Full logic preservation: This implementation maintains all validation checks from your Java 7 code, including the final stack-empty check.
Alternative: Using Reduce (For Immutable State)
If you prefer immutable state (though it’s less efficient here since we have to copy the stack each time), you can wrap the stack and validation status in a helper class and use reduce:
// Helper class to hold validation state private static class ValidationState { private final Deque<Character> stack; private final boolean isValid; private ValidationState(Deque<Character> stack, boolean isValid) { this.stack = stack; this.isValid = isValid; } // Process a single character and return a new state private ValidationState process(char c) { if (!isValid) return this; // Skip processing if already invalid if (isOpenToken(c)) { Deque<Character> newStack = new ArrayDeque<>(stack); newStack.push(c); return new ValidationState(newStack, true); } if (stack.isEmpty() || !matches(stack.pop(), c)) { return new ValidationState(stack, false); } return new ValidationState(stack, true); } } private static boolean execute(String expression) { ValidationState finalState = expression.chars() .mapToObj(c -> (char) c) .reduce(new ValidationState(new ArrayDeque<>(), true), ValidationState::process, (state1, state2) -> { // Parallel processing isn't practical here, so throw an error if used throw new UnsupportedOperationException("Parallel processing not supported for bracket validation"); }); return finalState.isValid && finalState.stack.isEmpty(); }
This approach is more functional but less efficient than the allMatch version because it copies the stack for each character. Stick with allMatch for most cases — it’s faster and closer to the original logic.
内容的提问来源于stack exchange,提问作者theyCallMeJun

