when to use map and forEach - java

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.

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.

Exctract the Stream from an Optional<Stream>

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())

How do I search a map in Java for a key that matches a predicate?

Do I understand this correctly? Is this how Java developers do it? Or is there a better way?
So if I want to find a key in a map that matches a predicate, I must first get the key set from the map through the conveniently supplied method. THEN, I have to convert the set into a stream through the conveniently supplied method. THEN, I have to filter the stream with my predicate through the conveniently supplied method. THEN, I have to convert the stream into a container of a type of my choosing, possibly supplying a collector to do so, through the conveniently supplied method. THEN, I can at least check the container for empty to know if anything matched. Only then can I use the key(s) to extract the values of interest, or I could have used the entry set from the beginning and spare myself the extra step.
Is this the way, really? Because as far as I can tell, there are no other methods either built into the map or provided as a generic search algorithm over iterators or some other container abstraction.
I prefer entrySet myself as well. You should find this efficient:
Map<String, Integer> map; //Some example Map
//The map is filled here
List<Integer> valuesOfInterest = map.entrySet()
.stream() //Or parallelStream for big maps
.filter(e -> e.getKey().startsWith("word")) //Or some predicate
.map(Map.Entry::getValue) //Get the values
.collect(Collectors.toList()); //Put them in a list
The list is empty if nothing matched. This is useful if multiple keys match the predicate.
In a nutshell, it is as simple as:
Predicate<T> predicate = (t -> <your predicate here>);
return myMap.keySet()
.stream()
.filter(predicate)
.findAny()
.map(myMap::get);
returns an empty Optional if no key matches
(nota: findAny is better than findFirst because it does not prevent parallelization if relevant, and findFirst is useless anyway since the Set of keys is not sorted in any meaningful way, unless your Map is a SortedMap)
It’s not clear why you are shouting “THEN” so often. It’s the standard way of solving problems, to combine tools designed for broad variety of use cases to get your specific result. There is a built-in capability for traversing a sequence of elements and search for matches, the Stream API. Further, the Map interface provides you with the Collection views, keySet(), entrySet(), and values(), to be able to use arbitrary tools operating on Collections, the bridge to the Stream API being one of them.
So if you have a Map<Key,Value> and are interested in the values, whose keys match a predicate, you may use
List<Value> valuesOfInterest = map.entrySet().stream()
.filter(e -> e.getKey().fulfillsCondition())
.map(Map.Entry::getValue)
.collect(Collectors.toList());
which consists of three main steps, filter to select matches, map to specify whether you are interested in the key, value, entry or a converted value of each matche and collect(Collectors.toList()) to specify that you want to collect the results into a List.
Each of these steps could be replaced by a different choice and the entire stream pipeline could be augmented by additional processing steps. Since you want this specific combination of operations, there is nothing wrong with having to specify exactly these three steps instead of getting a convenience method for your specific use case.
The initial step of entrySet().stream() is required as you have to select the entry set as starting point and switch to the Stream API which is the dedicated API for element processing that doesn’t modify the source. The Collection API, on the other hand, provides you with methods with might mutate the source. If you are willing to use that, the alternative to the code above is
map.keySet().removeIf(key -> !key.fulfillsCondition());
Collection<Value> valuesOfInterest=map.values();
which differs in that the nonmatching entries are indeed removed from the source map. Surely, you don’t want to confuse these two, so it should be understandable, why there is a clear separation between the Collection API and the Stream API.

Unable to iterate over the stream created (second time) with Steam.spliterator on an Iterable

I am unable to unable to iterate (second time) over the stream created with Steam.spliterator . I could not find documentation about the same.
Here is what i am doing:
I got a Iterable as funciton argument and I am iterating this via stream like following code :
StreamSupport.stream(values.spliterator(), false)
and following that i am doing it again but the second one do not iterate at all. I spent lot of time debugging it and finally converted the iterable to a list in the beginning itself.
Do any of you guys know the reason ?
Edit: Sorry if i am not clear ,
I was not using the stream multiple times , I was generating the stream in the above way with the same Iterable.
Iterable is the one coming from reduce in MapReduce job.
Thanks,
Hareendra
A Stream is a one-shot object. You can consume it only once, not multiple times. If you want to use the contents multiple times you have to do like you did, converting the stream to a list or array or anything non-streamy and then create two new streams out of it for the two things you want to do.
Quote from JavaDoc of Stream class:
A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, "forked" streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream.
Be sure that the Iterable instance you are using to create your Spliterator truly honors the Iterable contract. Some people make the mistake of thinking that anything that implements iterator() will serve as an Iterable, and that is not the case. To comply with the Iterable contract, one must be able to call iterator() multiple times and be able to iterate with it each time.
Because it is very easy to create an Iterable from anything that has an iterator() function, I have seen several cases of a manufactured Iterable exhibiting the behavior you mention. For example, one can do this:
Stream<String> stream = ...
Iterable<String> falseIterable = stream::iterator;
falseIterable does not follow required Iterable semantics because falseIterable.iterator(), being a wrapper around stream.iterator(), will not return a usable Iterator a second time, once it has been iterated over.

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