How to apply flatMap() to implement the following logic with Streams - java

I have the following for loop:
List<Player> players = new ArrayList<>();
for (Team team : teams) {
ArrayList<TeamPlayer> teamPlayers = team.getTeamPlayers();
for (teamPlayer player : teamPlayers) {
players.add(new Player(player.getName, player.getPosition());
}
}
and I'm trying to convert it to a Stream:
List<Player> players = teams.forEach(t -> t.getTeamPlayers()
.forEach(p -> players.add(new Player(p.getName(), p.getPosition())))
);
But I'm getting a compilation error:
variable 'players' might not have been initialized
Why is this happening? Maybe there's an alternative way to create the stream, I was thinking of using flatMap but not sure how to apply it.

First of all, you need to understand that Streams don't act like Loops.
Hence, don't try to mimic a loop. Examine the tools offered by the API. Operation forEach() is there for special cases when you need to perform side-effects, not in order to accumulate elements from the stream into a Collection.
Note: with teams.forEach() you're not actually using a stream, but method Iterable.forEach() which is available with every implementation of Iterable.
To perform reduction on streams, we have several specialized operations like collect, reduce, etc. (for more information refer to the API documentation - Reduction).
collect() operation is meant to perform mutable reduction. You can use to collect the data into a list by providing built-in Collector Collectors.toList() as an argument. And since Java 16 operation toList() was introduced into API, which is implemented on top of the toArray() operation and performs better than namesake collector (therefore it's a preferred option if your JDK version allows you to use it).
I was thinking of using flatMap but not sure how to apply it.
Operation flatMap() is meant to perform one-to-many transformations. It expects a Function which takes a stream element and generates a Stream of the resulting type, elements of the generated stream become a replacement for the initial element.
Note: that general approach to writing streams to use as fewer operations as possible (because one of the main advantages that Functional programming brings to Java is simplicity). For that reason, applying flatMap() when a stream element produces a Collection in a single step is idiomatic, since it's sorter than performing map().flatMap() in two steps.
That's how implementation might look like:
List<Team> teams = List.of();
List<Player> players = teams.stream() // Stream<Team>
.flatMap(team -> team.getTeamPlayers().stream()) // Stream<Player>
.map(player -> new Player(player.getName(), player.getPosition()))
.toList(); // for Java 16+ or collect(Collectors.toList())

This is basically the answer of Alexander Ivanchenko, but with method reference.
final var players = teams.stream()
.map(Team::getTeamPlayers)
.flatMap(Collection::stream)
.map(p -> new Player(p.getName(), p.getPosition()))
.toList();
If your Player class has a factory method like (depending on the relation between Player and TeamPlayer:
public static Player fromTeamPlayer(final TeamPlayer teamPlayer) {
return new Player(teamPlayer.getName(), teamPlayer.getPosition());
}
You could further reduce it to:
final var players = teams.stream()
.map(Team::getTeamPlayers)
.flatMap(Collection::stream)
.map(Player::fromTeamPlayer)
.toList();

Related

Java-Stream - Combine results of two methods producing a Value and a List invoked conditionaly inside the same stream operation in Java 8

I have two methods: funca() and funcb() which return a value of type X or a List<X> respectively like shown below:
X funca(Event e) { ... }
List<X> funcb(Event e) { ... }
I want to use them in the Stream and collect the result into a list.
These method methods should be called under different conditions, like shown below in pseudocode:
List<Event> input = // initializing the input
List<X> resultList = input.values().stream()
.(event -> event.status=="active" ? funca(event) : funcb(event))
.collect(Collectors.toList());
Can someone please tell me how I can achieve this so that whether the function returns a list of values or values?
Since one of your functions produces a Collection as a result, you need a stream operation that allows performing one-to-many transformation. For now, Stream IPA offers two possibilities: flatMap() and mapMulti().
To approach the problem, you need to take a closer look at them and think in terms of these operations.
flatMap()
This operation requires a function producing a Stream, and elements of this new stream become a replacement for the initial element.
Therefore, you need to wrap the result returned by the funca() with Singleton-Stream using Stream.of() (there's no need for wrapping the element with a List, like shown in another answer flatMap() is not capable to consume Collections).
List<X> = input.values().stream()
.flatMap(event -> "active".equals(event.getStatus()) ?
Stream.of(funca(event)) : funcb(event).stream()
)
.toList(); // for Java 16+ or collect(Collectors.toList())
mapMulti()
This operation was introduced with Java 16 and is similar to flatMap() but acts differently.
Contrary to flatMap it doesn't consume a new Stream. As an argument it expects a BiConsumer. Which in turn takes a stream element and a Consumer of the resulting type. Every element offered to the Consumer becomes a part of the resulting stream.
mapMulti() might be handy if funcb() produces a list which is very moderate in size (refer to documentation linked above for more details), otherwise flatMap() would be the right tool.
List<X> = input.values().stream()
.<X>mapMulti((event, consumer) -> {
if ("active".equals(event.getStatus())) consumer.accept(funca(event));
else funcb(event).forEach(consumer);
})
.toList(); // for Java 16+ or collect(Collectors.toList())
Sidenote: don't use == to compare reference types (like String) unless you need to make sure that both references are pointing to the same object, use equals() method instead.
Embed the result of funcA into a list and flatMap the lists:
List<X> result = input.stream()
.flatMap(e -> e.status.equals("active") ? List.of(funcA(e)) : funcB(e))
.collect(Collectors.toList());

Obtain from a List custom Objects of a List of distinct String values of Two String fields of these Objects using Java-Streams

Java stream on specific fields in a custom class object
I have an ArrayList of Train objects.
Each Train has three fields: source, destination, cost.
I want to get all the place names, i.e. all distinct sources + destinations.
I am using the below code, but as it can be observed, I'm using two streams to retrieve the data.
List<String> destinations = list.stream()
.map(x -> x.getDestination())
.distinct()
.collect(Collectors.toList());
List<String> sources = List.stream()
.map(x -> x.getSource())
.distinct()
.collect(Collectors.toList());
I was wondering how I could accomplish the same thing in a single stream? Can it be done using flatMap, or there's another way to achieve this?
List<String> allPlaces = ?
Also, is this possible to use Train class without getters?
You had the right idea with flatMap - you can map a train to a stream that contains the source and destination, and then flatMap it to you "main" stream:
List<String> allPlaces =
trains.stream()
.flatMap(t -> Stream.of(t.getSource(), t.getDestination()))
.distinct()
.collect(Collectors.toList());
In this case, we can utilize Java 16 method mapMulti(), which is functionally similar to flatMap(). It's meant for transforming a single stream element into a group of elements.
Here's how implementation might look like:
List<String> places = trains.stream()
.<String>mapMulti((train, consumer) -> {
consumer.accept(train.getSource());
consumer.accept(train.getDestination());
})
.distinct()
.toList();
Contrary to flatMap() it doesn't consume a stream, but operates via Consumer. mapMulti() a recommended alternative to flatMap() for situations when a new stream flatMap() requires would contain only a few elements (like in this case when we have only two elements: source and destination).
A quote from the API Note:
This method is preferable to flatMap in the following circumstances:
When replacing each stream element with a small (possibly zero)
number of elements. Using this method avoids the overhead of creating
a new Stream instance for every group of result elements, as required
by flatMap.
Addressing peripheral question:
Also is this possible without the getters methods of class Train?
Sure, you can. But it's not a recommended practice to access instance fields directly. In Java we're using access modifier to hide and protect member-variables within the class, that's one of the aspects of Encapsulation.

Creating an ImmutableList from an Iterable

I need to fetch an object from each element in an Iterable and add it into a List.
I am able to do this using the code below. However, are there any ways of creating a Guava ImmutableList without instantiating a List explicitly?
List<Data> myList = new ArrayList<>();
myIterable.forEach(val ->
myList.add(val.getMetaData())
);
To apply a function to each element and turn it into an ImmutableList, today's best practice would be
Streams.stream(myIterable).map(Value::getMetaData)
.collect(ImmutableList.toImmutableList());
are there any ways of creating an ImmutableList with instantiating the List explicitly?
Solution using standard JDK features
You can use StreamSupport.stream() to generate a stream out of your Iterable and then apply map() to transform to extract Data objects from stream elements and toList() to obtain an immutable list as the result:
List<Data> result = StreamSupport.stream(
myIterable.spliterator(), // spliterator
false // denotes whether the stream should be parallel or not
)
.map(MyClass::getMetaData)
.toList(); // for Java 8 .collect(Collectors.toUnmodifiableList())
A simple Demo
JDK Stream API & Guava ImmutableList
The code might look like that:
List<Data> result = StreamSupport.stream(
myIterable.spliterator(),
false
) // Stream<MyClass>
.map(MyClass::getMetaData) // Stream<Data>
.collect(
ImmutableList::<Data>builder, // accumulation type - ImmutableList.Builder<Data>
ImmutableList.Builder::add, // adding stream element into a builder
(left, right) -> left.addAll(right.build()) // merging builders while executing in parallel
)
.build(); // building an ImmutableList
To future readers: The last code snippet which makes use of ImmutableList.Builder provided for rather educational purposes. If you are not particularly interested in ImmutableList from Guava library (for instance, you're not using this library in your project) then have a look at the solution at the very beginning of the answer. If you do want Guava's ImmutableList for some reason, then the better option would be to use the approach provided in the answer by Louis Wasserman

How to update each element in a List in Java 8 using Stream API

I have a List defined as follows:
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
How can I increment each element of the List by one (i.e. end up with a List [2,3]) using Java 8's Stream API without creating new List?
When you create a Stream from the List, you are not allowed to modify the source List from the Stream as specified in the “Non-interference” section of the package documentation. Not obeying this constraint can result in a ConcurrentModificationException or, even worse, a corrupted data structure without getting an exception.
The only solution to directly manipulate the list using a Java Stream, is to create a Stream not iterating over the list itself, i.e. a stream iterating over the indices like
IntStream.range(0, list1.size()).forEach(ix -> list1.set(ix, list1.get(ix)+1));
like in Eran’s answer
But it’s not necessary to use a Stream here. The goal can be achieved as simple as
list1.replaceAll(i -> i + 1);
This is a new List method introduced in Java 8, also allowing to smoothly use a lambda expression. Besides that, there are also the probably well-known Iterable.forEach, the nice Collection.removeIf, and the in-place List.sort method, to name other new Collection operations not involving the Stream API. Also, the Map interface got several new methods worth knowing.
See also “New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8” from the official documentation.
Holger's answer is just about perfect. However, if you're concerned with integer overflow, then you can use another utility method that was released in Java 8: Math#incrementExact. This will throw an ArithmeticException if the result overflows an int. A method reference can be used for this as well, as seen below:
list1.replaceAll(Math::incrementExact);
You can iterate over the indices via an IntStream combined with forEach:
IntStream.range(0,list1.size()).forEach(i->list1.set(i,list1.get(i)+1));
However, this is not much different than a normal for loop, and probably less readable.
reassign the result to list1:
list1 = list1.stream().map(i -> i+1).collect(Collectors.toList());
public static Function<Map<String, LinkedList<Long>>, Map<String, LinkedList<Long>>> applyDiscount = (
objectOfMAp) -> {
objectOfMAp.values().forEach(listfLong -> {
LongStream.range(0, ((LinkedList<Long>) listfLong).size()).forEach(index -> {
Integer position = (int) index;
Double l = listfLong.get(position) - (10.0 / 100 * listfLong.get(position));
listfLong.set(position, l.longValue());
});
});
return objectOfMAp;
};

Java 8 Stream throwing exception about Stream being closed [duplicate]

I'd like to duplicate a Java 8 stream so that I can deal with it twice. I can collect as a list and get new streams from that;
// doSomething() returns a stream
List<A> thing = doSomething().collect(toList());
thing.stream()... // do stuff
thing.stream()... // do other stuff
But I kind of think there should be a more efficient/elegant way.
Is there a way to copy the stream without turning it into a collection?
I'm actually working with a stream of Eithers, so want to process the left projection one way before moving onto the right projection and dealing with that another way. Kind of like this (which, so far, I'm forced to use the toList trick with).
List<Either<Pair<A, Throwable>, A>> results = doSomething().collect(toList());
Stream<Pair<A, Throwable>> failures = results.stream().flatMap(either -> either.left());
failures.forEach(failure -> ... );
Stream<A> successes = results.stream().flatMap(either -> either.right());
successes.forEach(success -> ... );
I think your assumption about efficiency is kind of backwards. You get this huge efficiency payback if you're only going to use the data once, because you don't have to store it, and streams give you powerful "loop fusion" optimizations that let you flow the whole data efficiently through the pipeline.
If you want to re-use the same data, then by definition you either have to generate it twice (deterministically) or store it. If it already happens to be in a collection, great; then iterating it twice is cheap.
We did experiment in the design with "forked streams". What we found was that supporting this had real costs; it burdened the common case (use once) at the expense of the uncommon case. The big problem was dealing with "what happens when the two pipelines don't consume data at the same rate." Now you're back to buffering anyway. This was a feature that clearly didn't carry its weight.
If you want to operate on the same data repeatedly, either store it, or structure your operations as Consumers and do the following:
stream()...stuff....forEach(e -> { consumerA(e); consumerB(e); });
You might also look into the RxJava library, as its processing model lends itself better to this kind of "stream forking".
You can use a local variable with a Supplier to set up common parts of the stream pipeline.
From http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/:
Reusing Streams
Java 8 streams cannot be reused. As soon as you call any terminal operation the stream is closed:
Stream<String> stream = Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
stream.anyMatch(s -> true); // ok
stream.noneMatch(s -> true); // exception
Calling `noneMatch` after `anyMatch` on the same stream results in the following exception:
java.lang.IllegalStateException: stream has already been operated upon or closed
at
java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at
java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)
at com.winterbe.java8.Streams5.test7(Streams5.java:38)
at com.winterbe.java8.Streams5.main(Streams5.java:28)
To overcome this limitation we have to to create a new stream chain for every terminal operation we want to execute, e.g. we could create a stream supplier to construct a new stream with all intermediate operations already set up:
Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
streamSupplier.get().anyMatch(s -> true); // ok
streamSupplier.get().noneMatch(s -> true); // ok
Each call to get() constructs a new stream on which we are save to call the desired terminal operation.
Use a Supplier to produce the stream for each termination operation.
Supplier<Stream<Integer>> streamSupplier = () -> list.stream();
Whenever you need a stream of that collection,
use streamSupplier.get() to get a new stream.
Examples:
streamSupplier.get().anyMatch(predicate);
streamSupplier.get().allMatch(predicate2);
We've implemented a duplicate() method for streams in jOOλ, an Open Source library that we created to improve integration testing for jOOQ. Essentially, you can just write:
Tuple2<Seq<A>, Seq<A>> duplicates = Seq.seq(doSomething()).duplicate();
Internally, there is a buffer storing all values that have been consumed from one stream but not from the other. That's probably as efficient as it gets if your two streams are consumed about at the same rate, and if you can live with the lack of thread-safety.
Here's how the algorithm works:
static <T> Tuple2<Seq<T>, Seq<T>> duplicate(Stream<T> stream) {
final List<T> gap = new LinkedList<>();
final Iterator<T> it = stream.iterator();
#SuppressWarnings("unchecked")
final Iterator<T>[] ahead = new Iterator[] { null };
class Duplicate implements Iterator<T> {
#Override
public boolean hasNext() {
if (ahead[0] == null || ahead[0] == this)
return it.hasNext();
return !gap.isEmpty();
}
#Override
public T next() {
if (ahead[0] == null)
ahead[0] = this;
if (ahead[0] == this) {
T value = it.next();
gap.offer(value);
return value;
}
return gap.poll();
}
}
return tuple(seq(new Duplicate()), seq(new Duplicate()));
}
More source code here
Tuple2 is probably like your Pair type, whereas Seq is Stream with some enhancements.
You could create a stream of runnables (for example):
results.stream()
.flatMap(either -> Stream.<Runnable> of(
() -> failure(either.left()),
() -> success(either.right())))
.forEach(Runnable::run);
Where failure and success are the operations to apply. This will however create quite a few temporary objects and may not be more efficient than starting from a collection and streaming/iterating it twice.
Another way to handle the elements multiple times is to use Stream.peek(Consumer):
doSomething().stream()
.peek(either -> handleFailure(either.left()))
.foreach(either -> handleSuccess(either.right()));
peek(Consumer) can be chained as many times as needed.
doSomething().stream()
.peek(element -> handleFoo(element.foo()))
.peek(element -> handleBar(element.bar()))
.peek(element -> handleBaz(element.baz()))
.foreach(element-> handleQux(element.qux()));
cyclops-react, a library I contribute to, has a static method that will allow you duplicate a Stream (and returns a jOOλ Tuple of Streams).
Stream<Integer> stream = Stream.of(1,2,3);
Tuple2<Stream<Integer>,Stream<Integer>> streams = StreamUtils.duplicate(stream);
See comments, there is performance penalty that will be incurred when using duplicate on an existing Stream. A more performant alternative would be to use Streamable :-
There is also a (lazy) Streamable class that can be constructed from a Stream, Iterable or Array and replayed multiple times.
Streamable<Integer> streamable = Streamable.of(1,2,3);
streamable.stream().forEach(System.out::println);
streamable.stream().forEach(System.out::println);
AsStreamable.synchronizedFromStream(stream) - can be used to create a Streamable that will lazily populate it's backing collection, in a way such that can be shared across threads. Streamable.fromStream(stream) will not incur any synchronization overhead.
For this particular problem you can use also partitioning. Something like
// Partition Eighters into left and right
List<Either<Pair<A, Throwable>, A>> results = doSomething();
Map<Boolean, Object> passingFailing = results.collect(Collectors.partitioningBy(s -> s.isLeft()));
passingFailing.get(true) <- here will be all passing (left values)
passingFailing.get(false) <- here will be all failing (right values)
We can make use of Stream Builder at the time of reading or iterating a stream.
Here's the document of Stream Builder.
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.Builder.html
Use case
Let's say we have employee stream and we need to use this stream to write employee data in excel file and then update the employee collection/table
[This is just use case to show the use of Stream Builder]:
Stream.Builder<Employee> builder = Stream.builder();
employee.forEach( emp -> {
//store employee data to excel file
// and use the same object to build the stream.
builder.add(emp);
});
//Now this stream can be used to update the employee collection
Stream<Employee> newStream = builder.build();
I had a similar problem, and could think of three different intermediate structures from which to create a copy of the stream: a List, an array and a Stream.Builder. I wrote a little benchmark program, which suggested that from a performance point of view the List was about 30% slower than the other two which were fairly similar.
The only drawback of converting to an array is that it is tricky if your element type is a generic type (which in my case it was); therefore I prefer to use a Stream.Builder.
I ended up writing a little function that creates a Collector:
private static <T> Collector<T, Stream.Builder<T>, Stream<T>> copyCollector()
{
return Collector.of(Stream::builder, Stream.Builder::add, (b1, b2) -> {
b2.build().forEach(b1);
return b1;
}, Stream.Builder::build);
}
I can then make a copy of any stream str by doing str.collect(copyCollector()) which feels quite in keeping with the idiomatic usage of streams.

Categories