Exctract the Stream from an Optional<Stream> - java

Suppose I have an Optional containing a Stream:
Optional<Stream<Integer>> optionalStream = Optional.of(Stream.of(1, 2, 3));
Now I need to extract the Stream itself. If the Optional is empty, you want to get an empty Stream.
I'm looking of is something like flatStream() that performs transformation in one step. How can I do this?
My current attempt:
Stream<Integer> stream = optionalStream.stream().flatMap(Function.identity());
The Context of the Problem
In my real scenario, I have something like this, which gives me a Stream<Optional<Foo>>:
stream.findFirst().map(e -> e.getChildren())

Use Stream.empty()
It doesn't make sense to wrap a Stream with an Optional.
An Optional allows to interact safely with the result of the method call, which might not produce the data. And empty Optional represents the case when the data is absent.
A Stream can also be empty, and it represents the absents of data perfectly fine without a need of being packed into an Optional.
Use Stream.empty() as a return value for your method.
You also might want to check these references:
Uses for Optional
Valid usage of Optional type in Java 8
Addressing the Question-update
Optional.stream()
The best solution to the problem is to eliminate the problem.
The spirit of the answer remains unchanged: don't create an Optional<Stream<T>> and there would be no need to deal with it.
Java 9 Optional.stream() comes to the rescue. Apply stream() after findFirst(), and you would get either a singleton-stream containing a value captured by findFirst(), or an empty stream. And then you can perform any transformation you need.
So, instead of this:
stream.findFirst().map(e -> e.getChildren())
Do the following:
stream.findFirst().stream().flatMap(Foo::getChildren)
Stream.limit()
Alternatively, as suggested by #Holger instead of findFirt() you can apply limit(1) and continue to chain stream-operation. This approach would work with JDK 8.
stream.limit(1).flatMap(Foo::getChildren)

I guess you could use the orElseGet() method:
optionalStream.orElseGet(()->Stream.empty())

Related

Terminal operations on streams cannot be chained?

I have this concern when it is said there can be one terminal operation and terminal operations cannot be chained. we can write something like this right?
Stream1.map().collect().forEach()
Isn’t this chaining collect and forEach which are both terminal operations. I don’t get that part
The above works fine
Because
Assuming you meant collect(Collectors.toList()), forEach is a List operation, not a Stream operation. Perhaps the source of your confusion: there is a forEach method on Stream as well, but that's not what you're using.
Even if it weren't, nothing stops you from creating another stream from something that can be streamed, you just can't use the same stream you created in the first place.
Stream has forEach, and List has forEach (by extending Iterable). Different methods, but with the same name and purpose. Only the former is a terminal operation.
One practical difference is that the Stream version can be called on a parallel stream, and in that case, the order is not guaranteed. It might appear "random". The version from Iterable always happens on the same, calling thread. The order is guaranteed to match that of an Iterator.
Your example is terminally collecting the stream's items into a List, then calling forEach on that List.
That example is bad style because the intermediate List is useless. It's creating a List for something you could have done directly on the Stream.

Java 8: assigning a value from a stream to a variable?

I just started to learn streams in java8. I am trying to convert a basic for each loop into a stream that does the exact same thing. I have checked around here and found this:
Convert For-Loop into Java stream. The issue with this is that the example collects the results at the end and I don't think I need to collect anything but to assign the value which is not explained.
the loop which I want to convert is this. As you can see I want to assign the value to the vanTire.
VanTire vanTire = null;
for (Object tire : tireList) {
if (tire instanceof VanTire) {
vanTire = (VanTire) tire;
}
}
What I have so far with streams is this. How do I assign the value to the vanTire variable since filtering and casting is already done?
tireList.stream()
.filter(VanTire.class::isInstance)
.map(VanTire.class::cast)
After getting the correct items, you need to use .findFirst or .findAny to get an element, along with orElse to provide a default value
VanTire vanTire = (VanTire) tireList.stream().filter(VanTire.class::isInstance)
.findAny().orElse(null);
.elseGet and .elseThrow in Optional documentation
.findFirst or .findAny inStream documentation
In general, Streams should be stateless. This is due to the fact that the elements in Streams might be processed out of order. Thus, I would suggest a solution that does not assing the value from within the Stream, but rather yield a result from the stream that is assignable to the desired type:
VanTire vanTire = tireList.stream()
.filter(VanTire.class::isInstance)
.findAny()
.map(VanTire.class::cast)
.orElse(null);
I used .orElse(null) since this maps to the semantics of the sample code provided wrt. the behaviour when tireList is empty. Depending on the business case, however, continuing computation with an Optional or calling orElseThrow(...) might be a better option.

when to use map and forEach

I am learning Java 8 and came across a situation. Where in I have to iterate over a list of strings and then convert them to upperCase. The possible solutions would be to stream the list. Among many suggestions from Intellij the below two seems to be useful.
list.stream()
.map(String::toUpperCase)
or
list.stream().
forEach(p -> p.toUpperCase())
I am confused on which one to use and the use cases for all the Suggestions. Can I get help regarding which method to use and how to understand using all those suggestions?
Stream.map() will never run unless you end the pipeline in a terminal operation, like forEach(). But calling toUpperCase() in a forEach() won't do anything either, because strings are immutable. String.toUpperCase() doesn't change the string; it returns a new one.
If you just want to update the list in-place, you can use
list.replaceAll(String::toUpperCase);
which actually replaces each element with the result of the passed function.
If you want the results in a new list, use the map() snippet with a collector:
List<String> list2 = list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
forEach is an terminal operation that makes a difference through side effects. map is a non-terminal operation that makes a direct mapping from one element to another. For example, here is a canonical usage of forEach:
stream.forEach(System.out::println);
This will invoke, on each element of the stream, the equivalent of System.out.println(element);. However, the stream will be closed after this, and no operations may be executed on stream afterwards. map, on the other hand, may be used like this:
streamOfObjects.map(Object::toString).collect(Collectors.toList());
In this case, each Object within streamOfObjects is mapped to a String, created by invocation of toString. Then, the stream of Strings produced by map is collected into a List using a Collector.
In any case, I'd suggest using replaceAll for this use case, as suggested by #shmosel.
As for how to understand suggestions provided by autocomplete, I would strongly suggest reading JavaDocs on the related classes.

What it mean by the API when it says the interface's method returns something?

I understand that we cannot instantiate an interface but while doing the tutorial from a book regarding Streams, I got confused.
I will just use the portion of code to highlight the part which I don't understand.
// count occurences of each word in a Stream<String> sorted by word
Map<String, Long> wordCounts =
Files.lines(Paths.get("Chapter2Paragraph.txt"))
.map(line -> line.replaceAll("(?!')\\p{P}", ""))
.flatMap(line -> pattern.splitAsStream(line))
.collect(Collectors.groupingBy(String::toLowerCase,
TreeMap::new, Collectors.counting()));
For the method flatMap, when clicked upon in the API, it says it:
Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is null an empty stream is used, instead.)
So what does it mean by that? I kinda understand what it does but I simply don't understand how it works behind the scenes. Does it return an Object when the API mentions returns in this case or does it mean that it replaces the current stream? Additionally, when using Streams, does the compiler actually creates an Object of these elements and then terminates when it's done?
Additionally, from the code above, I just want to make sure that I'm correct.
When you have a Map<String, Long> wordCounts variable, does it mean that at the end of the stream termination, the end result has to follow exactly the type inference?
flapMap() turns each element into a stream (of any type). The streams are concatenated together to make one big stream.
In your example, the entire file is streamed (as one stream) after each line is split on the pattern (not specified in the question).
You appear to have analyzed the situation mostly correctly, but perhaps you are hazy about some of the intermediate steps.
You have a chain of methods that are called on the return value of the previous method in the chain. The return value of the final method (collect) is stored in the Map named wordCounts. Disregarding exactly what the methods do and what they return (just for a moment), this is standard behavior when you call a chain of methods like that.
The generic type of the map is determined by String::toLowerCase and Collectors.counting() in the final method call. The former specifies the key type to be String and the latter specifies the values to be Long. If, as an example, you had used String::length as the key instead, you would have gotten a map of type Map<Integer, Long> instead, which would have counted the number of occurrences of words with a given length.
Going back to the sequence of function calls, it can be broken down as follows:
Files.lines(Path) creates a Stream<String> of the lines in a file. Since the result is a stream, you can now call...
Stream.map(Function<String, String>) transforms the input stream of strings into another stream of strings using the call to line.replaceAll(...).
The stream of edited lines now gets Stream.flatMap(Function<String, Stream<String>>) to it to split the lines into words and return a single continuous stream. Remember that pattern.splitAsStream will be applied to each line in sequence, and so will return as many streams as there are lines. Stream.flatMap takes all those streams and strings them out into a single continuous stream.
Note that the whole purpose of encapsulation is that you don't have to know exactly how the process works under the hood. You only need to know what the final result is (in this case a Stream<String>). You should be able to swap an implementation that reads all the streams into an underlying collection up-front and returns a stream from that with one that opens each stream lazily as elements are processed without having to worry about what is really happening.
Now that you have a Stream<String> of words in the file, you apply what is called the terminal operation: Stream.collect(Collector<String, String, Map<String, Long>>). The collector is created by Collectors.groupingBy(Function<String, String>, Supplier<Map<String, String>>, Collector<String, String, Long>). This creates a collector that groups the input stream into sub-streams according to the key returned by the classifier Function (String.toLowerCase()) and passes it into a "downstream" collector to do the actual accumulation on each sub-stream. The resulting accumulation is stored into the map returned by the Supplier (TreeMap::new). The downstream Collector is created by Collectors.counting(), which just counts the number of elements in each stream.
I have expanded all the generic types in this description to make it easier to follow along and see the kinds of objects that result from each step.
On a more general note, streams in Java have two types of operations: intermediate and terminal. The stream comes from a source (in this case your file). All the intermediate operations (1-3) turn one stream into another stream. The input and output types are always clearly defined, as I have shown above, just like for any other operation. The terminal operation is one that returns a single value of some sort based on the stream. In your case, you count the word frequencies and stash them into a Map. This is pretty well documented in the java.util.stream package summary.

Java 8 forEach use cases

Let's say you have a collection with some strings and you want to return the first two characters of each string (or some other manipulation...).
In Java 8 for this case you can use either the map or the forEach methods on the stream() which you get from the collection (maybe something else but that is not important right now).
Personally I would use the map primarily because I associate forEach with mutating the collection and I want to avoid this. I also created a really small test regarding the performance but could not see any improvements when using forEach (I perfectly understand that small tests cannot give reliable results but still).
So what are the use-cases where one should choose forEach?
map is the better choice for this, because you're not trying to do anything with the strings yet, just map them to different strings.
forEach is designed to be the "final operation." As such, it doesn't return anything, and is all about mutating some state -- though not necessarily that of the original collection. For instance, you might use it to write elements to a file, having used other constructs (including map) to get those elements.
forEach terminates the stream and is exectued because of the side effect of the called Cosumer. It does not necessarily mutate the stream members.
map maps each stream element to a different value/object using a provided Function. A Stream <R> is returned on which more steps can act.
The forEach terminal operation might be useful in several cases: when you want to collect into some older class for which you don't have a proper collector or when you don't want to collect at all, but send you data somewhere outside (write into the database, print into OutputStream, etc.). There are many cases when the best way is to use both map (as intermediate operation) and forEach (as terminal operation).

Categories

Resources