Is it possible to rewrite a complicated for loop like this just using java 8 streams? Anything I come up with seems more bloated, then just leaving the code as is below with a normal for loop.
public static boolean isBalanced(String text) {
int count = 0;
for(int i = 0; i < text.length(); i++ ) {
if (text.charAt(i) == ')') {
count--;
} else if (text.charAt(i) == '(') {
count++;
}
if (count < 0) {
return false;
}
}
return count == 0;
}
Using Streams
public static boolean isBalanced2(String text) {
AtomicInteger count = new AtomicInteger(0);
text.chars()
.forEachOrdered(x -> {
if (x == ')') {
count.getAndDecrement();
} else if (x == '(') {
count.getAndIncrement();
}
});
return count.get() == 0;
}
It works ok but it iterates through the whole string, when sometimes that might be wasted computation for example in the case of the string ")......"
It doesn't seem possible to exit the stream as soon as count is < 0 ? (And I dont want to throw an exception!)
Thanks
You should not.
Lambda and Stream are not replacement for all complicated for loop. While you may use a Stream as you've done, this does not means it is better for the eye (what is easier to understand?) and for performance (you surely lost something due to the AtomicInteger vs int based operation but you could probably use a int[] array instead).
filter after map operation but that won't make it easier to read.AtomicInteger).The following code will do what you want, and is smaller than your original code, but it's convoluted and always processes all characters, i.e. doesn't stop early if unbalanced ) is detected.
However, unlike some other answers here, it doesn't violate the stream rules by maintaining state outside the stream.
private static boolean isBalanced(String text) {
return 0 == text.chars()
.reduce(0, (n, c) -> n < 0 ? n : c == '(' ? n + 1 : c == ')' ? n - 1 : n);
}
The logic is as follows:
Keep a running total representing the nesting level, i.e. increment the value when ( is found and decrement the value when ) is found.
If the total goes below 0, stop updating it, i.e. when an unbalanced ) is found, keep final total at -1.
The result of the reduce operation is then:
0: All ( have balanced )
-1: Found unbalanced )
>0: Found unbalanced (
Long version of the same code, using if statements instead of conditional ternary operator.
private static boolean isBalanced(String text) {
int finalLevel = text.chars().reduce(0, (lvl, ch) -> {
if (lvl < 0)
return lvl; // Keep result of -1 for unbalanced ')'
if (ch == '(')
return lvl + 1;
if (ch == ')')
return lvl - 1;
return lvl;
});
return (finalLevel == 0);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With