Lambdas vs Iterators in Java - java

When writing Java code, NetBeans often encourages me to convert foreach loops (with iterators) to lambda expressions. Sometimes the resulting code is much cleaner. Other times, the result is less clear than before.
For example, the following uses iterators:
List<String> list = new ArrayList<>();
for (String str : list) {
if (str.charAt(0) == ' ')) {
// do something with str
}
}
And the equivalent uses lambda expressions:
List<String> list = new ArrayList<>();
list.stream().filter((str) -> (str.charAt(0) == ' ')).forEach((str) -> {
// do something with str
)};
In this case, using lambda expressions results in lengthier code, and uses less intuitive language (stream, filter, and forEach instead of for and if). So are there any advantages to using lambdas instead of iterators, when the code isn't cleaner? For example, are there any performance gains?

Iterate over collection and doing "something" with some of it's members is not the best example for using lambdas.
Consider scenario like create result collection of objects filtered by some property, ordered by some other property and do some other "magic" and you'll see that lambdas saves dozens lines of code.
Hard to say if it's easier to read (probably not as lambda is yet another syntax you have to be familiar with) but after all - it's better to read one complex line of code instead creating anonymous/inner comparators. At least in C# lambda is very useful construction.

uses less intuitive language (stream, filter, and forEach instead of for and if
I don't really find them less intuitive. Moreover, wait for few months, those will be mostly used terms you will hear in Java. In fact, once you are comfortable with lambdas, you'll see how amazingly it cleansifies your code, that uses complex logic inside loops.
So are there any advantages to using lambdas instead of iterators, when the code isn't cleaner? For example, are there any performance gains?
One obvious advantage of streams and lambdas that comes to my mind is, it gives you the power of parallel execution more easily using Stream.parallelStream(). Also internal iteration of streams gives control of how iteration happens to the API. It can choose to evaluate intermediate operations lazily, parallely, sequentially, etc. Moreover, functional programming has it's own advantage. You can pass around logics in the form of lambdas, which used to be done using anonymous classes earlier. That way, some functionality can be easily re-used.
Although there are certain disadvantages too, when comparing with normal for loop. If your loop is modifying some local variable, then you can't get it converted to forEach and lambdas version (at least not directly), because variables used inside lambdas needs to be effectively final.
More details can be found on java.util.stream javadoc.

Just to give you some information about how I think you can write neat lambdas, is for your given code:
List<String> listString = new ArrayList<>();
for (String str : listString) {
if (str.charAt(0) == ' ') {
// do something with str
}
}
I would, convert it to the following:
List<String> listString = new ArrayList<>();
listString.stream()
.filter(s -> s.charAt(0) == ' ')
.forEach(/*do something*/);
I find the syntax like this much less intrusive and much clearer. Also, if you are going to need blocks inside your lambda, you are probably doing it wrong. Unless you have a very good reason to do so.
As an example if you would want to print every string on a new line, you could do:
List<String> listString = new ArrayList<>();
listString.stream()
.filter(s -> s.charAt(0) == ' ')
.forEach(System.out::println);
Moreover, if you need to store the data in some kind of structure, you want to be using Collectors.*, and you do not want to be doing that inside your forEach, as a silly example we would like to convert it to a List<String> again:
List<String> listString = new ArrayList<>();
List<String> filteredString = listString.stream()
.filter(s -> s.charAt(0) == ' ')
.collect(Collectors.toList());
Note that this particular implemention could have been done much easier if you were allowed to modify the original list.

With respect to performance gains, I have written a simple benchmark test for lambda expressions versus iterators. The test gives performance during warmup phase and non warmup phase. Warmup phase is considered for the first 10 iterations, non warmup phase later on. The test runs for 10k iterations and measures the average time. Below is the code:
import java.util.ArrayList;
import java.util.List;
public class LambdaTest {
private static final int COUNT = 10000;
public static void main(String[] args) {
List<String> str = new ArrayList<String>();
for (int i =0; i<100000; i++){
str.add(""+i);
}
double iterTotal = 0;
double lambdaTotal = 0;
double warmupIterTotal = 0;
double warmupLambdaTotal = 0;
for(int j = 0; j < COUNT; j++){
long start = System.nanoTime();
for (int i = 0; i< 100000; i++){
String string = str.get(i);
// if(string.length() < 5){
// System.out.println(string);
// }
}
long end = System.nanoTime();
if(j>=10){
iterTotal += end - start;
}else {
warmupIterTotal += end - start;
}
System.out.println("Output 1 in : "+(end-start)*1.0/1000 +" Us");
start = System.nanoTime();
str.forEach((string) -> {
// if(string.length() < 5){
// System.out.println(string);
// }
});
end = System.nanoTime();
if(j>=10){
lambdaTotal+= end-start;
}else {
warmupLambdaTotal += end - start;
}
System.out.println("Output 2 in : "+(end-start)*1.0/1000 +" Us");
}
System.out.println("Avg Us during warmup using Iter: "+warmupIterTotal/(1000*10));
System.out.println("Avg Us during warmup using Lambda: "+warmupLambdaTotal/(1000*10));
System.out.println("Avg Us using Iter: "+iterTotal/(1000*(COUNT-10)));
System.out.println("Avg Us using Lambda: "+lambdaTotal/(1000*(COUNT-10)));
}
}
The output of the above code is as below:
Avg Us during warmup using Iter: 1372.8821
Avg Us during warmup using Lambda: 5211.7064
Avg Us using Iter: 373.6436173173173
Avg Us using Lambda: 370.77465015015014
Thus, as we can see the lambda expressions perform poorly in the warmup stages but post-warmup the performance with lambda expressions is quite similar to iterators. This is just a simple test and if you are trying out a complex expression in your application, you can better profile the application and check which one works better in your case.

Related

Java takeWhile statefulness

I'm writing a parser for a programming language, and I was thinking Java's takeWhile method might be perfect for grouping the lexical tokens into structures. As an example case, to get a stream of tokens representing a brace-delimited code block, I could write:
stream.dropWhile(t -> !t.equals("{")).takeWhile(t -> !t.equals("}")))
…Except, obviously that wouldn't work if the block in question had nested blocks, such as a function declaration. In a more imperative style, I might do this after the opening brace:
assert(openCurlyToken.equals("{");
List blockTokens = new ArrayList<String>();
blockTokens.add(openCurlyToken);
String token;
int braceLevel = 1;
while (braceLevel > 0) {
token = nextToken();
if (token.equals("{")) braceLevel++;
else if (token.equals("}")) braceLevel--;
blockTokens.add(t);
}
return blockTokens;
Kinda ugly, kinda verbose. What I'm hoping is there is a more concise way to do this kind of computation using Streams?

Calculate all permutations of a collection in parallel

I need to calculate all permutations of a collection and i have a code for that but the problem is that it is linear and takes a lot of time.
public static <E> Set<Set<E>> getAllCombinations(Collection<E> inputSet) {
List<E> input = new ArrayList<>(inputSet);
Set<Set<E>> ret = new HashSet<>();
int len = inputSet.size();
// run over all numbers between 1 and 2^length (one number per subset). each bit represents an object
// include the object in the set if the corresponding bit is 1
for (int i = (1 << len) - 1; i > 0; i--) {
Set<E> comb = new HashSet<>();
for (int j = 0; j < len; j++) {
if ((i & 1 << j) != 0) {
comb.add(input.get(j));
}
}
ret.add(comb);
}
return ret;
}
I am trying to make the computation run in parallel.
I though of the option to writing the logic using recursion and then parallel execute the recursion call but i am not exactly sure how to do that.
Would appreciate any help.
There is no need to use recursion, in fact, that might be counter-productive. Since the creation of each combination can be performed independently of the others, it can be done using parallel Streams. Note that you don’t even need to perform the bit manipulations by hand:
public static <E> Set<Set<E>> getAllCombinations(Collection<E> inputSet) {
// use inputSet.stream().distinct().collect(Collectors.toList());
// to get only distinct combinations
// (in case source contains duplicates, i.e. is not a Set)
List<E> input = new ArrayList<>(inputSet);
final int size = input.size();
// sort out input that is too large. In fact, even lower numbers might
// be way too large. But using <63 bits allows to use long values
if(size>=63) throw new OutOfMemoryError("not enough memory for "
+BigInteger.ONE.shiftLeft(input.size()).subtract(BigInteger.ONE)+" permutations");
// the actual operation is quite compact when using the Stream API
return LongStream.range(1, 1L<<size) /* .parallel() */
.mapToObj(l -> BitSet.valueOf(new long[] {l}).stream()
.mapToObj(input::get).collect(Collectors.toSet()))
.collect(Collectors.toSet());
}
The inner stream operation, i.e. iterating over the bits, is too small to benefit from parallel operations, especially as it would have to merge the result into a single Set. But if the number of combinations to produce is sufficiently large, running the outer stream in parallel will already utilize all CPU cores.
The alternative is not to use a parallel stream, but to return the Stream<Set<E>> itself instead of collecting into a Set<Set<E>>, to allow the caller to chain the consuming operation directly.
By the way, hashing an entire Set (or lots of them) can be quite expensive, so the cost of the final merging step(s) are likely to dominate the performance. Returning a List<Set<E>> instead can dramatically increase the performance. The same applies to the alternative of returning a Stream<Set<E>> without collecting the combinations at all, as this also works without hashing the Sets.

How do you use stream operations to calculate an average of values within a list, omitting some?

I have a method that returns the average of a property over a number of model objects:
List<Activity> activities = ...;
double effortSum = 0;
double effortCount = 0;
activities.stream().forEach(a -> {
double effort = a.getEffort();
if (effort != Activity.NULL) {
effortCount++; < Compilation error, local variable
effortSum += effort; < Compilation error, local variable
}
});
But, the above attempt doesn't compile, as noted. The only solution that's coming to me is using an AtomicReference to a Double object, but that seems crufty, and adds a large amount of confusion to what should be a simple operation. (Or adding Guava and gaining AtomicDouble, but the same conclusion is reached.)
Is there a "best practice" strategy for modifying local variables using the new Java 8 loops?
Relevant code for Activity:
public class Activity {
public static final double NULL = Double.MIN_VALUE;
private double effort = NULL;
public void setEffort(double effort) { this.effort = effort; }
public double getEffort() { return this.effort; }
...
}
Is there a "best practice" strategy for modifying local variables using the new Java 8 loops?
Yes: don't. You can modify their properties -- though it's still a bad idea -- but you cannot modify them themselves; you can only refer to variables from inside a lambda if they are final or could be final. (AtomicDouble is indeed one solution, another is a double[1] that just serves as a holder.)
The correct way of implementing the "average" operation here is
activities.stream()
.mapToDouble(Activity::getEffort)
.filter(effort -> effort != Activity.NULL)
.average()
.getAsDouble();
In your case, there is a solution that is more functional - just compute the summary statistics from the stream from where you can grab the number of elements filtered and their sum:
DoubleSummaryStatistics stats =
activities.stream()
.mapToDouble(Activity::getEffort)
.filter(e -> e != Activity.NULL)
.summaryStatistics();
long effortCount = stats.getCount();
double effortSum = stats.getSum();
Is there a "best practice" strategy for modifying local variables
using the new Java 8 loops?
Don't try do to that. I think the main issues is that people try to translate their code using the new Java 8 features in an imperative way (like in your question - and then you have troubles!).
Try to see first if you can provide a solution which is functional (which is what the Stream API aim for, I believe).

Java 8 - Streams Nested ForEach with different Collection

I try to understand the new Java 8 Streams and I tried for days to transfer nested foreach loops over collection in Java 8 Streams.
Is it possible to refactor the following nested foreach loops including the if-conditions in Java-8-Streams?
If yes what would it look like.
ArrayList<ClassInq> Inq = new ArrayList<>();
TreeMap<String, SalesQuot> Quotations = new TreeMap<>();
ArrayList<ClassInq> tempInqAndQuot = new ArrayList<>();
ArrayList<SalesQuot> tempQuotPos = new ArrayList<>();
for(ClassInq simInq : this.Inq) {
if(!simInq.isClosed() && !simInq.isDenied()) {
for(Map.Entry<String, SalesQuot> Quot: Quotations.entrySet()) {
SalesQuot sapQuot = Quot.getValue();
if(sapQuot.getInquiryDocumentNumber().compareTo(simInq.getSapInquiryNumber()) == 0) {
simInq.setSAPQuotationNumber(sapQuot.getQuotationDocumentNumber());
tempInqAndQuot.add(simInq);
for(Map.Entry<String, SalesQuotPosition> quotp : sapQuot.getPosition().entrySet()) {
tempQuotPos.add(quotp.getValue());
}
}
}
}
}
Thanks a lot for your help.
BR
First, try to adhere to the Java naming conventions, as your upper case variable names make it really hard to read your code. Second, it’s a good thing that you want to learn about Stream API but you should not ignore the basics of the pre-Java 8 Collection APIs.
It’s not useful to iterate over an entrySet() when you are only interested in either, keys or values. You do it two times within a small piece of code.
At the first appearance you can replace
for (Map.Entry<String, SalesQuot> Quot: Quotations.entrySet()){
SalesQuot sapQuot = Quot.getValue();
with the simpler
for (SalesQuot sapQuot: Quotations.values()){
At the second, the entire
for(Map.Entry<String,SalesQuotPosition> quotp: sapQuot.getPosition().entrySet()){
tempQuotPos.add(quotp.getValue());
}
can be replaced by
tempQuotPos.addAll(sapQuot.getPosition().values());
Thus even without streams, your code can be simplified to
for (ClassInq simInq : this.Inq){
if (!simInq.isClosed() && !simInq.isDenied()){
for (SalesQuot sapQuot: Quotations.values()){
if (sapQuot.getInquiryDocumentNumber().compareTo(simInq.getSapInquiryNumber()) == 0){
simInq.setSAPQuotationNumber(sapQuot.getQuotationDocumentNumber());
tempInqAndQuot.add(simInq);
tempQuotPos.addAll(sapQuot.getPosition().values());
}
}
}
}
though it’s still not clear what it is supposed to do and whether it’s correct. Besides the errors and suspicions named in the comments to your question, modifying the incoming values (esp. from the outer loop) does not look right.
It’s also not clear why you are using ….compareTo(…)==0 rather than equals.
However, it can be straight-forwardly rewritten to use streams without changing any of the code’s logic:
this.Inq.stream().filter(simInq -> !simInq.isClosed() && !simInq.isDenied())
.forEach(simInq -> Quotations.values().stream().filter(sapQuot ->
sapQuot.getInquiryDocumentNumber().compareTo(simInq.getSapInquiryNumber())==0)
.forEach(sapQuot -> {
simInq.setSAPQuotationNumber(sapQuot.getQuotationDocumentNumber());
tempInqAndQuot.add(simInq);
tempQuotPos.addAll(sapQuot.getPosition().values());
})
);
Still, I recommend cleaning up the original logic first before rewriting it for using other APIs. The stream form would greatly benefit from a more precise definition of what to achieve.

Is it possible to write a loop in Java that does not actually use a loop method?

I was curious if, in Java, you could create a piece of code that keeps iterating a piece of code without the use of a for or while loop, and if so, what methods could be used to solve this?
Look at recursion. A recursive function is a function which calls itself until a base case is reached. An example is the factorial function:
int fact(int n)
{
int result;
if(n==1)
return 1;
result = fact(n-1) * n;
return result;
}
You could use the Java 8 Streams methods for iterating over the elements of a Collection. Among the methods you can use are filtering methods (get all the elements of a collection that satisfy some conditions), mapping methods (map a Collection of one type to a Collection of another type) and aggregation methods (like computing the sum of all the elements in a Collection, based on some integer member of the Element stored in the collection).
For example - Stream forEach :
List<Element> = new ArrayList<Element>();
...
list.stream().forEach (element -> System.out.println(element));
Or you can do it without a Stream :
List<Element> = new ArrayList<Element>();
...
list.forEach (element -> System.out.println(element));
Another variant of recursion:
public class LoopException extends Exception {
public LoopException(int i, int max) throws LoopException {
System.out.println( "Loop variable: "+i);
if (i < max)
throw new LoopException( i+1, max );
}
}
Of course this is just a bit of fun, don't ever do it for real.
Java does not have a goto statement (that's a lie), so that way is a dead end.
But you could always make a piece of code endlessly iterate using recursion. Old factorial function seems to be the favorite, but since it is not an infinite loop, I will go for this simple function:
int blowMyStack(int a) {
return blowMyStack(a + 1);
}
There will be many ways to do this using various features of the language. But it always falls to an underlying recursion.
In case you're referring of something like C's goto, the answer is no.
In other cases, you can use recursive functions.

Categories