why java stream "reduce()" accumulates the same object - java

ComparisonResults comparisonResults = requestsList
.parallelStream()
.map(item -> getResponse(item))
.map(item -> compareToBl(item))
.reduce(new ComparisonResults(), (result1, result2) ->
{
result1.addSingleResult(result2);
return result1;
});
when
private ComparisonResults compareToBl(CompleteRoutingResponseShort completeRoutingResponseShortFresh) {
...
ComparisonResults comparisonResults = new ComparisonResults();
...
return comparisonResults;
}
however when I debug:
.reduce(new ComparisonResults(), (result1, result2) ->
{
result1.addSingleResult(result2);
return result1;
});
I see result1 and result2 are always the same object (object id in the
IDEA)
result1 equals result2

addSingleResult should return a new object as modified a copy of this so you should change your code to:
.reduce(new ComparisonResults(), (result1, result2) ->
{
return result1.addSingleResult(result2);
});
Otherwise, you are always returning the same instance (without modifications).
From Java documentation:
The reduce operation always returns a new value. However, the
accumulator function also returns a new value every time it processes
an element of a stream. Suppose that you want to reduce the elements
of a stream to a more complex object, such as a collection. This might
hinder the performance of your application. If your reduce operation
involves adding elements to a collection, then every time your
accumulator function processes an element, it creates a new collection
that includes the element, which is inefficient. It would be more
efficient for you to update an existing collection instead. You can do
this with the Stream.collect method, which the next section describes.

Related

Initializing a String on basis on java streams results in an empty String [duplicate]

The JavaDoc for Stream.collect() says that it returns "the result of the reduction". That doesn't tell me if code like this can return null for filteredList:
List<String> filteredList = inputList.stream()
.filter(c -> c.isActive())
.collect(Collectors.toList());
I would expect that if it could return null then it would return an Optional, but it doesn't say that either.
Is it documented anywhere whether Stream.collect() can return null?
Collector.toList() will return an empty List for you.
Here is the implementation:
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
As you can see ArrayList::new is being used as a container for your items.
From JavaDoc of Collector:
A mutable reduction operation that
accumulates input elements into a mutable result container, optionally
transforming the accumulated result into a final representation after
all input elements have been processed. Reduction operations can be
performed either sequentially or in parallel.
A Collector is specified by four functions that work together to
accumulate entries into a mutable result container, and optionally
perform a final transform on the result. They are:
creation of a new result container (supplier())
incorporating a new data element into a result container (accumulator())
combining two result containers into one (combiner())
performing an optional final transform on the container (finisher())
And
A sequential implementation of a reduction using a collector would
create a single result container using the supplier function, and
invoke the accumulator function once for each input element. A
parallel implementation would partition the input, create a result
container for each partition, accumulate the contents of each
partition into a subresult for that partition, and then use the
combiner function to merge the subresults into a combined result.
So as long as you don't do weird things like combine function return null, the Collector always return at least a mutable container using your provided supplier function.
And I think it's very counter-intuitive if an implementation would ever return null container.
This is not dependent on Stream.collect, but on the individual Collector. Collectors.toList() will return an empty ArrayList.
That said, there's no reason someone couldn't use a weird Collector to return null in certain circumstances:
.collect(
Collector.of(
ArrayList::new,
ArrayList::add,
(a, b) -> {
a.addAll(b);
return a;
},
a -> a.isEmpty() ? null : a // finisher replaces empty list with null
)
);
So the Collector is the thing you need to remember to check. I believe all of the Collectors available out-of-the-box will return empty collections, as you'd expect.
You could use Collectors::collectingAndThen to pass collect() result to a Function<T,R>. Return value of the Function<T,R> will be return value of collect().
List<String> filteredList = inputList.stream()
.filter(c -> c.isActive())
.collect(Collectors.collectingAndThen(Collectors.toList(), c -> !c.isEmpty()?c:null));
I think this part of the documentation says that it cannot be null:
Returns a Collector that accumulates the input elements into a new
List.
Highlights added by me. I think this new List means that something that isn't null.
I started to check ReferencePipeline.collect() to check whether it's true for the actual implementation. Unfortunately, it was a futile attempt. There are so many cases here, like is it parallel? is it after a forEach? etc.
This is collector-dependant. The one You're using (Collectors.toList()) returns an empty list.
No collect will never return null, in order to check use isEmpty() instead of null
I believe the following code is a good implementation if you really need to return null when the list is empty (assuming the type of the variable c is MyObj):
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
...
List<String> filteredList = inputList.stream()
.filter(MyObj::isActive)
.collect(collectingAndThen(toList(), Stream::of)
.filter(List::isEmpty)
.findAny()
.orElse(null);

Java: is there more elegant way to extract a new list from an existing list using reduce?

I'm trying to take a list of elements, make some manipulation on part of these elements and put the output of these manipulations in a new list.
I want to do it with only one iteration on the list.
The only way I found to do it is:
List<Integer> newList = numList.stream().reduce(new ArrayList<Integer>(),
(acc, value) -> {
if (value % 2 == 0) {
acc.add(value * 10);
}
return acc;
},
(l1, l2) -> {
l1.addAll(l2);
return l1;
}
);
As you can see, it's very cumbersome.
I can of course use filter and then map, but in this case I iterate the list twice.
In other languages (Javascript for example) this kind of reduce operation is very straightforward, for example:
arr.reduce((acc, value) => {
if (value % 2 == 0) {
acc.push(value * 10);
}
return acc;
}, new Array())
Amazing!
I was thinking to myself whether Java has some nicer version for this reduce or the Java code I wrote is the shortest way to do such an operation.
I can of course use filter and they map, but in this case I iterate the list twice.
That's not true. The elements of the Stream are iterated once, no matter how many intermediate operations you have.
Besides, the correct way to perform mutable reduction is to use collect:
List<Integer> newList =
numList.stream()
.filter(v -> v % 2 == 0)
.map(v -> v * 10)
.collect(Collectors.toList());
What you're doing is the opposite of functional programming, because you're using functions only to get a side effect.
You can simply not use reduce to make it even more explicit:
List<Integer> newList = numList.stream()
.filter(value -> value % 2 == 0)
.map(value -> value * 10)
.collect(Collectors.toList());
Edit: Streams iterate over the collection once by definition. According to the javadoc:
A stream should be operated on (invoking an intermediate or terminal stream operation) only once.

JAVA-STREAM : re-use double a DoubleStream

I need a trick to solve this problem i'm using Java 1.8 And I retrieved a an object from a method that returns a DoubleStream object. So, the problem is I could not reuse the stream after it has been consumed.
Here is the first version of the code :
DoubleStream stream = object.getvalueStream(a,b);
if(condtion)
stream.map(v -> v * 2);
stream.forEach(value -> {
// do something
}
The problem is that once the condition is true, the stream is consumed And I can not reuse it. So I tried to use a supplier to return a doubleStream from supplier and iterate overt it.
But still the same problem as I try to recover the stream from my stream object which is already used.
Here is my updated code :
DoubleStream stream = object.getvalueStream(a,b);
if(condtion)
stream.map(v -> v * 2);
Supplier>DoubleStream> streamSupplier = () -> DoubleStream.of(stream.toArray());
streamSupplier.get().forEach(value -> {
//Do something
But I still have the same problem since I create my supplier from my stream already used if the condition is true.
Thanks for your help.
once the condition is true, the stream is consumed And I can not reuse it
Intermediate operations (e.g. map) return a new stream, so you need to reassign the stream after the intermediate operation (map).
I.e.
DoubleStream stream = object.getvalueStream(a,b);
if (condition) {
stream = stream.map(v -> v * 2);
}
stream.forEach(value -> {
// do something
}
Note terminal operations (e.g. foreach) do not return a stream. So if you want many terminal operations, you should collect the stream so it can be reused.
Note also, there is also an intermediate version of foreach called peek if you wish to chain foreach (peek) calls.
Streams in Java are not up to be reused. You should collect the result and stream twice like
List<Double> doubles = object.getvalueStream(a,b).boxed().collect(toList());
if(condition) {
doubles = doubles.stream().map(v -> v * 2).boxed().collect(toList);
}
// and further processing here
doubles.stream().forEach(v ->
...
);
You can use map() if it is important for you to keep the stream without collecting it. The only drawback in this approach is that you have to check the condition each time
DoubleStream stream = object.getvalueStream(a,b).map(v-> condition ? v*2 : v).forEach(...);
Or just assign the right Stream to the variable
DoubleStream stream = condition ? object.getvalueStream(a,b).map(v->v*2) : object.getvalueStream(a,b).map(v->v*2).forEach(...);

Java 8 list processing - add elements conditionally

I have the following piece of code:
List<Object> list = new ArrayList<>();
list.addAll(method1());
if(list.isEmpty()) { list.addAll(method2()); }
if(list.isEmpty()) { list.addAll(method3()); }
if(list.isEmpty()) { list.addAll(method4()); }
if(list.isEmpty()) { list.addAll(method5()); }
if(list.isEmpty()) { list.addAll(method6()); }
return list;
Is there a nice way to add elements conditionally, maybe using stream operations? I would like to add elements from method2 only if the list is empty otherwise return and so on.
Edit: It's worth to mention that the methods contain heavy logic so need to be prevented from execution.
You could try to check the return value of addAll. It will return true whenever the list has been modified, so try this:
List<Object> list = new ArrayList<>();
// ret unused, otherwise it doesn't compile
boolean ret = list.addAll(method1())
|| list.addAll(method2())
|| list.addAll(method3())
|| list.addAll(method4())
|| list.addAll(method5())
|| list.addAll(method6());
return list;
Because of lazy evaluation, the first addAll operation that added at least one element will prevent the rest from bein called. I like the fact that "||" expresses the intent quite well.
I would simply use a stream of suppliers and filter on List.isEmpty:
Stream.<Supplier<List<Object>>>of(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6())
.map(Supplier<List<Object>>::get)
.filter(l -> !l.isEmpty())
.findFirst()
.ifPresent(list::addAll);
return list;
findFirst() will prevent unnecessary calls to methodN() when the first non-empty list is returned by one of the methods.
EDIT:
As remarked in comments below, if your list object is not initialized with anything else, then it makes sense to just return the result of the stream directly:
return Stream.<Supplier<List<Object>>>of(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6())
.map(Supplier<List<Object>>::get)
.filter(l -> !l.isEmpty())
.findFirst()
.orElseGet(ArrayList::new);
A way of doing it without repeating yourself is to extract a method doing it for you:
private void addIfEmpty(List<Object> targetList, Supplier<Collection<?>> supplier) {
if (targetList.isEmpty()) {
targetList.addAll(supplier.get());
}
}
And then
List<Object> list = new ArrayList<>();
addIfEmpty(list, this::method1);
addIfEmpty(list, this::method2);
addIfEmpty(list, this::method3);
addIfEmpty(list, this::method4);
addIfEmpty(list, this::method5);
addIfEmpty(list, this::method6);
return list;
Or even use a for loop:
List<Supplier<Collection<?>>> suppliers = Arrays.asList(this::method1, this::method2, ...);
List<Object> list = new ArrayList<>();
suppliers.forEach(supplier -> this.addIfEmpty(list, supplier));
Now DRY is not the most important aspect. If you think your original code is easier to read and understand, then keep it like that.
You could make your code nicer by creating the method
public void addAllIfEmpty(List<Object> list, Supplier<List<Object>> method){
if(list.isEmpty()){
list.addAll(method.get());
}
}
Then you can use it like this (I assumed your methods are not static methods, if they are you need to reference them using ClassName::method1)
List<Object> list = new ArrayList<>();
list.addAll(method1());
addAllIfEmpty(list, this::method2);
addAllIfEmpty(list, this::method3);
addAllIfEmpty(list, this::method4);
addAllIfEmpty(list, this::method5);
addAllIfEmpty(list, this::method6);
return list;
If you really want to use a Stream, you could do this
Stream.<Supplier<List<Object>>>of(this::method1, this::method2, this::method3, this::method4, this::method5, this::method6)
.collect(ArrayList::new, this::addAllIfEmpty, ArrayList::addAll);
IMO it makes it more complicated, depending on how your methods are referenced, it might be better to use a loop
You could create a method as such:
public static List<Object> lazyVersion(Supplier<List<Object>>... suppliers){
return Arrays.stream(suppliers)
.map(Supplier::get)
.filter(s -> !s.isEmpty()) // or .filter(Predicate.not(List::isEmpty)) as of JDK11
.findFirst()
.orElseGet(Collections::emptyList);
}
and then call it as follows:
lazyVersion(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6());
method name for illustration purposes only.

Cartesian product using Java Streams

I have a cartesian product function in JavaScript:
function cartesianProduct(arr) {
return arr.reduce(function(a,b) {
return a.map(function(x) {
return b.map(function(y) {
return x.concat(y);
});
}).reduce(function(a,b) { return a.concat(b); }, []);
}, [[]]);
}
So that if I have a 3D array:
var data = [[['D']], [['E'],['L','M','N']]];
The result of cartesianProduct(data) would be the 2D array:
[['D','E'], ['D','L','M','N']]
What I'm trying to do is write this cartesian product function in Java using Streams.
So far I have the following in Java:
public Collection<Collection<String>> cartesianProduct(Collection<Collection<Collection<String>>> arr) {
return arr.stream().reduce(new ArrayList<Collection<String>>(), (a, b) -> {
return a.stream().map(x -> {
return b.stream().map(y -> {
return Stream.concat(x.stream(), y.stream());
});
}).reduce(new ArrayList<String>(), (c, d) -> {
return Stream.concat(c, d);
});
});
}
I have a type checking error that states:
ArrayList<String> is not compatible with Stream<Stream<String>>
My guesses as to what is wrong:
I need to use a collector somewhere (maybe after the Stream.concat)
The data type for the identity is wrong
This is possible with a bit of functional programming magic. Here's method which accepts Collection<Collection<Collection<T>>> and produces Stream<Collection<T>>:
static <T> Stream<Collection<T>> cartesianProduct(Collection<Collection<Collection<T>>> arr)
{
return arr.stream()
.<Supplier<Stream<Collection<T>>>> map(c -> c::stream)
.reduce((s1, s2) -> () -> s1.get().flatMap(
a -> s2.get().map(b -> Stream.concat(a.stream(), b.stream())
.collect(Collectors.toList()))))
.orElseGet(() -> () -> Stream.<Collection<T>>of(Collections.emptyList()))
.get();
}
Usage example:
cartesianProduct(
Arrays.asList(Arrays.asList(Arrays.asList("D")),
Arrays.asList(Arrays.asList("E"), Arrays.asList("L", "M", "N"))))
.forEach(System.out::println);
Output:
[D, E]
[D, L, M, N]
Of course instead of .forEach() you can collect the results to the List if you want to return Collection<Collection<T>> instead, but returning Stream seems more flexible to me.
A bit of explanation:
Here we create a stream of stream suppliers via map(c -> c::stream). Each function of this stream may produce by demand a stream of the corresponding collection elements. We do this because streams a once off (otherwise having stream of streams would be enough). After that we reduce this stream of suppliers creating a new supplier for each pair which flatMaps two streams and maps their elements to the concatenated lists. The orElseGet part is necessary to handle the empty input. The last .get() just calls the final stream supplier to get the resulting stream.

Categories

Resources