``` widgets.stream() .filter(b -> b.getColor() == RED) .mapToInt(b -> b.getWeight()) .sum(); ``` https://docs.oracle.com/javase/8/docs/api/java/util/stream/p...
They aren't specific to Java. In fact they require features such as lambdas that made them less practical until Java 8.
It's arguably more powerful in that it's got some more bits and bobs out-of-the-box, and it's easier to do parallel stuff with it. But it's also arguably less powerful in that it's not particularly open for anyone who isn't a JDK developer to adapt for their own purposes. To make your own Python iterator that will interact nicely with anything else that's designed to operate on iterators, you only have one method you have to implement. To make your own implementation of Java's Stream interface, you've got a rather daunting list of 40 you have to implement.
For example, as a 5-year Clojure convert who wrote Java for 15 years before that, old-fashioned loops are less readable now, as they hide the essentials of what is really happening. For example, you might be mapping a collection from one type of value to another, or reducing it to some other form, etc. But both use loops. You have to scan all over the place in the loop construct to see what it's really doing. I'd much rather see the function that is happening than the procedure by which the function is accomplished.
Option A:
static <X, Y, Z> Map<X, Z> transform(Map<? extends X, ? extends Y> input,
Function<Y, Z> function) {
return input.keySet().stream()
.collect(Collectors.toMap(Function.identity(),
key -> function.apply(input.get(key))));
}
Option B: static <X, Y, Z> Map<X, Z> transform1(Map<? extends X, ? extends Y> input,
Function<Y, Z> function) {
Map<X, Z> result = new HashMap<>();
input.forEach((k, v) -> result.put(k, function.apply(v)));
return result;
}
Personally I find Option B more readable. Another example:Option A:
protected boolean isCashBasis(CalendarDate effectiveDate) {
return priorVatReturns.stream()
.filter(r -> r.period.contains(effectiveDate))
.findFirst()
.map(r -> r.basis == AccountingBasis.CASH_BASIS)
.orElseGet(() -> super.isCashBasis(effectiveDate));
}
Option B: protected boolean isCashBasis(CalendarDate effectiveDate) {
for (VatReturn r : priorVatReturns) {
if (r.period.contains(effectiveDate)) {
return r.basis == AccountingBasis.CASH_BASIS;
}
}
return super.isCashBasis(effectiveDate);
}
Again I would choose Option B, although I suppose Option A isn't too bad.