This question already has answers here:
Why does Java8 Stream generate nothing?
(3 answers)
Closed 4 years ago.
When I use count() function "inside test" message called three times as expected but when I remove count() function call test() function doesn't called. From count() function documentation I understand that it returns the count of elements in given stream.
public class Start {
public static int test(int input) {
System.out.println("inside processRecord");
return input;
}
public static void main(String[] args) throws InterruptedException {
List<Integer> data = Arrays.asList(1,2,3);
data.parallelStream().map(Start::test).count();
}
}
because count is a terminal operation, and streams are invoked/executed only when a terminal one is present; they are said to be lazy...
Just notice that in java-9 and above, your example would not print those statements from map either way, since all you care about is how many and map is sort of useless...
Intermediate operations (like map) return a stream and are being invoked by terminal operations that return non-stream values.
Related
I want to reuse stream object in my method, but when I call second time from supplier I saw stream has already been operated upon or closed error.
private Stream<User> getUserStream(Stream<User> testStream) {
Supplier<Stream<User>> supplierUserStream=()->userStream;
supplierUserStream.get().sorted();
supplierUserStream.get().sorted();// throw exception Stream has already been operated upon or closed
return userStream;
}
I don't want to convert stream to a list. I saw a lot of examples like that.
Supplier<Stream<User>> streamSupplier
= () -> Stream.of(userStream.toArray(size -> new User[size]));
Look at the message closely (emphasis mine):
java.lang.IllegalStateException: stream has already been operated
upon or closed
Since there is no terminal operation in your first statement, your stream has not been closed but has been operated upon which is the reason why you are getting the error when you try to operate on it the second time.
You can change it as follows to get rid of the error:
supplierUserStream.get().sorted().sorted();
ONLINE DEMO
However, it will be a kind of hiding a potential problem e.g. if you replace the test call in main, with the following statement, you will again get the same problem:
getStringStream(Stream.of("Hello", "World")).forEach(System.out::println);
A good way to deal with it can be as suggested by ernest_k:
It can also be resolved by keeping track of derived stream objects.
Stream stream = s.get().sorted(); stream = stream.sorted();
return stream.sorted();, to reuse those redundant sort() calls.
Demo:
import java.util.function.Supplier;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
getStringStream(Stream.of("World", "Hello", "Good morning!")).forEach(System.out::println);
}
private static Stream<String> getStringStream(Stream<String> testStream) {
Supplier<Stream<String>> supplierStringStream = () -> testStream;
Stream<String> stream = supplierStringStream.get().sorted();
return stream.sorted();
}
}
Output:
Good morning!
Hello
World
This question already has answers here:
Difference between `Optional.orElse()` and `Optional.orElseGet()`
(9 answers)
When I need to use Optional.orElseGet() over Optional.orElse()
(3 answers)
Closed 2 years ago.
I was playing with Java's Optional and thought that it works like an if else block. But in the following code even if the name variable is not null the content of the orElse block get's executed. Any explanation?
import java.util.Optional;
class Scratch {
public static void main(String[] args) {
String name = "some text";
System.out.println(
Optional.ofNullable(name)
.map(n -> mapped())
.orElse(getOrElse()));
}
static String mapped(){
System.out.println("mapped -- block executed");
return "mapped";
}
static String getOrElse(){
System.out.println("orElse -- block executed");
return "orElse";
}
}
Output:
mapped -- block executed
orElse -- block executed
mapped
Have a closer look at Optional.orElse(T). It takes a value which needs to be provided to that call but that value will only be returned in the else case. So getOrElse() will be executed to get that value in any case.
Optional.orElse(T) is normally used with already existing values, e.g. like this: map(e -> mapped()).orElse("not found")
If you want to use a function that will only be called in the else case you need to use Optional.orElseGet(Supplier<? extends T>) like this: map(e -> mapped).orElseGet(Scratch::getOrElse) (or orElseGet(() -> getOrElse())).
the orElse part is not being executed. Your getOrElse method is being executed in order to pass a value into orElse, but when the Optional is evaluated the orElse branch isn't retruned.
If you want to lazily evaluate it, you could use orElseGet and pass a method reference to it:
Optional.ofNullable(name)
.map(n -> mapped())
.orElseGet(Scratch::getOrElse));
Running the following code sample ends with:
"Exception in thread "main" java.lang.StackOverflowError"
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class TestStream {
public static void main(String[] args) {
Stream<String> reducedStream = IntStream.range(0, 15000)
.mapToObj(Abc::new)
.reduce(
Stream.of("Test")
, (str , abc) -> abc.process(str)
, (a , b) -> {throw new IllegalStateException();}
);
System.out.println(reducedStream.findFirst().get());
}
private static class Abc {
public Abc(int id) {
}
public Stream<String> process(Stream<String> batch) {
return batch.map(this::doNothing);
}
private String doNothing(String test) {
return test;
}
}
}
What exactly is causing that issue? Which part of this code is recursive and why?
Your code isn't recursively looping. You can test with smaller numbers for the IntStream range (i.e. 1 or 100). In your case it's the actual stack size limit that causes the problem. As pointed out in some of the comments, its the way the streams are processes.
Each invocation on the stream creates a new wrapper stream around the original one. The 'findFirst()' method asks the previous stream for elements, which in turn asks the previous stream for elements. As the streams are no real containers but only pointers on the elements of the result.
The wrapper explosion happens in the reduce methods' accumulator '(str , abc) -> abc.process(str)'. The implementation of the method creates a new stream wrapper on the result (str) of the previous operation, feeding into the next iteration, creating a new wrapper on the result(result(str))). So the accumulation mechanism is one of a wrapper (recursion) and not of an appender (iteration). So creating a new stream of the actual (flattened) result and not on reference to the potential result would stop the explosion, i.e.
public Stream<String> process(Stream<String> batch) {
return Stream.of(batch.map(this::doNothing).collect(Collectors.joining()));
}
This method is just an example, as your original example doesn't make any sense because it does nothing, and neither does this example. Its just an illustration. It basically flattens the elements of the stream returned by the map method into a single string and creates a new stream on this concrete string and not on a stream itself, thats the difference to your original code.
You could tune the stacksize using the '-Xss' parameter which defines the size of the stack per thread. The default value depends on the platform, see also this question 'What is the maximum depth of the java call stack?' But take care when increasing, this setting applies to all threads.
This question already has answers here:
Why is this java Stream operated upon twice?
(4 answers)
Closed 6 years ago.
I would like to implement a pagination in stream api.
I used if statement to modify the stream, but I was surprise when I encountered
java.lang.IllegalStateException: stream has already been operated upon or closed
Here is my code
#Override
public List<Foo> search(String[] names, Integer offset, Integer size) {
final Stream<Foo> fooStream = findAllFoos().stream();
if(offset!=null) {
fooStream
.skip(offset)
.limit(size);
}
if(ArrayUtils.isNotEmpty(names)) {
fooStream.filter(foo -> ArrayUtils.contains(names, foo.getName()));
}
final List<Foo> foos = fooStream.collect(Collectors.toList()); //this lines throws the exception, according to stacktrace
return foos;
}
Here is my Foo.class
public class Foo {
private String name;
//..setters and getters
}
Here is sample call to that search method
fooService.search(fooNamesArray, 0, 1);
fooService.search(fooNamesArray, null, null);
fooStream.skip(offset)
does nothing by itself. It returns a new Stream; it does not modify fooStream.
Generally speaking you can only use a Stream for one operation, though that might be a transformation into another Stream.
What you could do, though it would still make your code awkward, would be to write fooStream = fooStream.skip(offset).limit(size).
You may want to add all your validation before hand and combine all stream operations in one go.
List<Foo> foos = fooStream.skip(offset)
.limit(size)
.filter(foo -> ArrayUtils.contains(names, foo.getName()))
.collect(Collectors.toList());
In other case, you can take the result of each stream operation into another stream and operate on them.
Stream<Foo> skippedFooStream = Collections.<Foo>emptyList().stream();
if(offset!=null) {
skippedFooStream = fooStream
.skip(offset)
.limit(size);
}
if(ArrayUtils.isNotEmpty(names)) {
// take this into another temp stream and proceed
skippedFooStream.filter(foo -> ArrayUtils.contains(names, foo.getName()));
}
This question already has answers here:
Limit a stream by a predicate
(19 answers)
Closed 8 years ago.
I have a set and a method:
private static Set<String> set = ...;
public static String method(){
final String returnVal[] = new String[1];
set.forEach((String str) -> {
returnVal[0] += str;
//if something: goto mark
});
//mark
return returnVal[0];
}
Can I terminate the forEach inside the lambda (with or without using exceptions)?
Should I use an anonymous class?
I could do this:
set.forEach((String str) -> {
if(someConditions()){
returnVal[0] += str;
}
});
but it wastes time.
implementation using stream.reduce
return set.parallelStream().reduce((output, next) -> {
return someConditions() ? next : output;
}).get(); //should avoid empty set before
I am looking for the fastest solution so exception and a 'real' for each loop are acceptable if they are fast enough.
I'm reluctant to answer this even though I'm not entirely sure what you're attempting to accomplish, but the simple answer is no, you can't terminate a forEach when it's halfway through processing elements.
The official Javadoc states that it is a terminal operation that applies against all elements in the stream.
Performs an action for each element of this stream.
This is a terminal operation.
If you want to gather the results into a single result, you want to use reduction instead.
Be sure to consider what it is a stream is doing. It is acting on all elements contained in it - and if it's filtered along the way, each step in the chain can be said to act on all elements in its stream, even if it's a subset of the original.
In case you were curious as to why simply putting a return wouldn't have any effect, here's the implementation of forEach.
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
The consumer is explicitly passed in, ad this is done independently of the actual iteration going on. I imagine you could throw an exception, but that would be tacky when more elegant solutions likely exist.