Are the lambda expressions evaluated at the place where we write them or in any other class of Java?
For example :
Stream<Student> absent = students.values().stream().filter(s -> !s.present());
Will the above lambda expression passed to the filter method be executed immediately in a given class where the code is written OR in another class and will it take some more time (in terms of nano seconds) than if the code was written in conventional coding style prior to Java 8?
When you compile your sources, the compiler will insert an invokedynamic byte code instruction for the lambda expression that you use. The actual implementation (which in your case is a Predicate) will be created at runtime via ASM. It will not even be present on hard disk when you run it - meaning the class is generated in memory, there will be no .class file for Predicate. That's a big difference between an anonymous class for example - that will generate a class file when you compile it.
You can see the generated file for the Predicate if you run your example with :
-Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here
Otherwise Eran's answer is correct, Streams are driven by the terminal operation, if such is not present nothing gets executed. You should absolutely read the excellent Holger's answer about even more interesting differences.
The body of the lambda expression passed to the filter method in your example won't be executed at all, since filter is an intermediate operation, which only gets executed for Streams that end in a terminal operation, such as collect, forEach, etc...
If you add a terminal operation, such as collecting the elements of the Stream to a List:
List<Student> absent = students.values().stream().filter(s -> !s.present()).collect(Collectors.toList());
the body of the lambda expression will be executed for each element of your Stream, in order for the terminal operation to be able to produce its output.
Note that this behavior would not change if you passed an anonymous class instance or some other implementation of the Predicate interface to your filter method instead of the lambda expression.
The expressions are lazy evaluated, which means they'll only actually be evaluated when you actually try to 'terminate' the stream - i.e. use an operation that takes a stream but returns something else, like collect, min, max, reduce, etc. Operations which take a stream as input and return a stream as output are usually lazy.
Lambda expressions are essentially objects with a single method, so they're evaluated whenever that method is called.
In your particular case they're never evaluated. A Stream does not evaluate the expressions until you call a terminating operation (collect, findAny, etcetera)
Related
I tried everything to get 100% of coverage on this lambda method but no matter what I do I don't get it.
private String createMessage(List<FieldError> erros) {
return erros.stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage, (keyOld, keyNew) -> keyOld)).toString();
}
The uncovered code is the implementation of a lambda expression. You only have one lambda expression (keyOld, keyNew) -> keyOld, which means that that code doesn't get executed.
The lambda expression is the 3rd argument to Collectors.toMap(), i.e. BinaryOperator<U> mergeFunction, which is documented as "a merge function, used to resolve collisions between values associated with the same key".
If there are no collisions in the data, the lambda expression won't get executed, so make sure you test the code with data where the erros list contains 2 or more elements with the same getField() value.
I have been learning java for past few months and just started to get into lambda functions. I recently switched my IDE and noticed a warning saying "Can be replaced with method reference" on codes like this.
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.forEach(num -> doSomething(num));
voiddoSomething(int num) {
System.out.println("Number is: " + num);
}
After some digging, I realized that instead of the line
intList.forEach(num -> doSomething(num));
I can just use
intList.forEach(this::doSomething);
This is just amazing. A few days ago I did not even knew about lambdas and was using for loops to do operations like this. Now I replaced my for loops with lambdas and even better, I can replace my lambdas with method references. The problem is that I don't really understand how all this works internally. Can anyone please explain or provide a good resource explaining how the doSomething function is called and the argument is passed to it when we use method reference?
The double-colon operator is simply a convenience operator for doing the same thing that your lambda is doing. Check out this page for more details: https://javapapers.com/core-java/java-method-reference/
The double colon is simply syntactic sugar for defining a lambda expression whose parameters and return type are the same as an existing function. It was created to to allow lambdas to more easily be added with existing codebases.
Calling the forEach method of a List<Integer> object takes as its parameter any object implementing the Consumer functional interface. Your lambda num -> doSomething(num) itself happens to fulfill the formal requirements of this interface.
Thus, you can use the double colon as syntactic sugar for that lambda expression.
In general, if you have an object obj with method func, which accepts parameters params... then writing obj::func is equivalent to the lambda (params...) -> obj.func(params...).
In your case, o is this (the current object), which has a method doSomething(), which takes an integer parameter, thus, this::doSomething is equivalent to num -> doSomething(num).
Given you've mentioned that it's only until recently you started getting into functional programming I'd like to keep things as simple and straightforward as possible, but note that with just the little code you've provided, we could derive a lot both from the high-level view of things as well the low-level view.
Can anyone please explain or provide a good resource explaining how
the doSomething function is called and the argument is passed to it
when we use method reference?
how the doSomething function is called is left to the library (internal iteration) regardless of whether we use a method reference or a lambda expression, so essentially we specify the what not the how meaning we provide to the forEach method a behaviour (a function) that we want to execute for each element of the source intList and not necessarily how it should go about its work.
This is then left to the library to apply (execute) the specified function of doSomething for each element of the source intList.
Method references can be seen as a shorthand for lambdas calling only a specific method. The benefit here is that by referring to a specific method name explicitly, your code gains better readability, therefore, making it easier to read and follow and in most cases reading code with method references reads as the problem statement which is a good thing.
It's also important to know that not any given function can be passed to the forEach terminal operation as every method that accepts a behaviour has a restriction on the type of function allowed. This is accomplished with the use of functional interfaces in the java.util.function package.
Lastly but not least, in terms of refactoring it's not always possible to use method references nor is it always better to use lambdas expressions over code that we used prior to Java-8. However, as you go on with your journey of learning the Java-8 features, a few tips to better your code are to try:
Refactoring anonymous classes to lambda expressions
Refactoring lambda expressions to method references
Refactoring imperative-style data processing to streams
According to this question, peek() is lazy it means we should somehow activate it. In fact, to activate it to print something out to the console I tried this :
Stream<String> ss = Stream.of("Hi","Hello","Halo","Hacker News");
ss.parallel().peek(System.out::println);
System.out.println("lol"); // I wrote this line to print sth out to terminal to wake peek method up
But that doesn't work and the output is :
lol
Thus, how can I make the peek function actually work?
If there is no way to that so whats the point of using peek?
You have to use terminal operation on a stream for it to execute (peek is not terminal, it is an intermediate operation, that returns a new Stream), e.g. count():
Stream<String> ss = Stream.of("Hi","Hello","Halo","Hacker News");
ss.parallel().peek(System.out::println).count();
Or replace peek with forEach (which is terminal):
ss.parallel().forEach(System.out::println);
peek() method uses Consumer as parameter which means that potentially you can mutate the state of the incoming element. At the same time Java documentation says that peek should be mostly used for debugging purposes. It is an intermediate operator and requires a terminal operator like forEach.
stream().peek(Consumer).forEach(Consumer);
Is it ok to process and count processed data in such way?
long count = userDao.findApprovedWithoutData().parallelStream().filter(u -> {
Data d = dataDao.findInfoByEmail(u.getEmail());
boolean ret = false;
if (d != null) {
String result = "";
result += getFieldValue(d::getName, ". \n");
result += getFieldValue(d::getOrganization, ". \n");
result += getFieldValue(d::getAddress, ". \n");
if(!result.isEmpty()) {
u.setData(d.getInfo());
userDao.update(u);
ret = true;
}
}
return ret;
}).count();
So, in short: iterate over not complete records, update if data is present and count this number of records?
IMHO this is bad code, because:
The filter predicate has (quite significant) side effects
Predicates should not have side effects (just like getters shouldn't). It's unexpected, and that makes it bad.
The filter predicate is very inefficient
Each execution of the predicate causes a large chain of queries to fire, which makes this code not scaleable.
At first glance, the main purpose seems to be getting a count, but really that's a minor (dispensable) bit of info
Good code makes it obvious what is going on (unlike this code)
You should change the code to use a (fairly simple) single update query (that employs a join) and get the count from the "number of rows updated" info in the result from the persistence API.
It depends on your definition of process . I cannot give you a clear yes or no because, I think it is hard to conclude without understanding your code and how it is implemented.
You are using Parallel Stream and what happens there is Java runtime splits the Stream into sub-streams based on number of available threads in ForkJoinPool's common pool.
When using parallelism you need to be careful for possible side effects:
Interference (Lambda expression in a stream should not interfere)
Lambda expressions in stream operations should not interfere.
Interference occurs when the source of a stream is modified while a
pipeline processes the stream.
Statetful Lambda expressions
Avoid using stateful lambda expressions as parameters in stream
operations. A stateful lambda expression is one whose result depends
on any state that might change during the execution of a pipeline.
Looking at your question and applying the above points to it.
Non-interference > strongly states that Lambda expressions should not interfere with the source of stream (unless stream source is concurrent) during pipeline operation because it can cause:
Exception (i.e. ConcurrentModificationException)
Incorrect Answer
Nonconformant behaviour
With exception of well-behaved streams where the modification takes place during intermediate operation (i.e. filter), read more in here.
Your Lambda expression does interfere with the source of the stream, which is not advised but, the interference is within Intermediate operation and now everything comes down to whether the stream is well-behaved or not. So you might consider re-thinking your lambda expression when it comes to interference. It might also come down to how you update the source of the stream via userDao.udpate, which is not clear from your question.
Stateful Lambda Expression > Your Lambda expression does not seem to be stateful and that is because the result of Lambda depends on value/s that do not change during the execution of the pipeline. So this does not apply to your case.
I advise you go through the documentation of Java 8 Stream as well as this blog which explains Java 8 Stream really well with examples.
When we use methods like filter, mapToInt, sum, etc. and
pass them lambda expressions I don't understand if the operations
are method themselves or are the lambda that we pass.
I' d like to know the correct terminology.
I think that the lambda is the function and thus the operation that we pass to
methods that use that to produce a results.
Why is also said that filter, sum, etc. are operations that use function as
their arguments?
Are both correct terminology?
Both the Stream methods and the lambda arguments that they accept are, broadly speaking, operations. This isn't confusing once we get used to the idea that the arguments to method calls can be functions. A Stream method applies the function that it's been given to the values in its stream, either to produce a new stream (intermediate methods) or to produce some aggregated result (terminal methods).
For a more detailed explanation, see http://www.lambdafaq.org/why-are-lambda-expressions-being-added-to-java/
Not sure if this is commonly accepted, but I think it is thus:
A function is something that receives arguments and produces a value, ideally without side effects (though, that is not enforcable in Java). Use this if you want to emphasize the mathematical/functional aspect.
A subroutine/procedure is a named piece of code that is reused for it's side effect.
A method is how functions and subroutines are implemented/written in Java. There is no such thing as a function or procedure that does not belong to some class.
A lambda expression in Java is a way to write methods (of some anonymous class that happens to implement a functional interface) on the fly and at the same time obtain a reference to an instance of said interface.
An operation is a function or procedure.
So, depending on how you want to look at it: Since it is about Java, you could just call everything "method". But sometimes you want to emphasize different aspects. Like in your example:
filter, sum, etc. are operations that use function as their arguments
Here, we could say: "filter is a method that takes a reference to a functional interface as argument", but this somehow changes the intention of the sentence.
Lambda is a function/callback which is usually passed as an argument. An approach to understand this, consider you need to search for a value in a list:
Java 7:
int i = Arrays.asList(1,2,3,4,5,6).indexOf(3);
This will give a single element. Now what happens, when one needs a more dynamic request - he could collect these items with a for-loop. But if he wants to do it in a similar fashion, he could pass a lambda as an argument:
Java 8 (how it could be):
List<Integer> collect = asList(1, 2, 3, 4, 5, 6).filter(x -> x >= 2 && x < 4);
the real Java 8 API example is a bit more verbose:
List<Integer> collect = Arrays.asList(1, 2, 3, 4, 5, 6)
.stream()
.filter(x -> x >= 2 && x < 4)
.collect(Collectors.toList());
Remember,
what we pass to the filter is as a function is just a filtering logic. While the filter function already expect a function that has the filtering logic. Filter function specifically accepts a function that returns boolean and decides whether to pass on the currently element in the stream or simply discard it.
I hope I have understood your question correctly.
For more on streams here is a ongoing series of article on Java 8 Stream API
amitph.com > Java 8 Streams API Tutorials