Java Streams - Sort if Comparator exists - java

I have a class where optionally a Comparator can be specified.
Since the Comparator is optional, I have to evaluate its presence and execute the same stream code, either with sorted() or without:
if(comparator != null) {
[...].stream().map()[...].sorted(comparator)[...];
} else {
[...].stream().map()[...];
}
Question:
Is there a more elegant way to do this without the code duplication?
Note:
A default Comparator is not an option, I just want to keep the original order of the values I am streaming.
Besides, the elements are already mapped at the point of sorting, so I can not somehow reference the root list of the stream, as I do not have the original elements anymore.

You can do something like this:
Stream<Something> stream = [...].stream().map()[...]; // preliminary processing
if(comparator != null) {
stream = stream.sorted(comparator); // optional sorting
}
stream... // resumed processing, which ends in some terminal operation (such as collect)

Another way would be to use Optional:
Stream<Whatever> stream = [...].stream().map()[...];
List<WhateverElse> result = Optional.ofNullable(comparator)
.map(stream::sorted)
.orElse(stream)
.[...] // <-- go on with the stream pipeline
.collect(Collectors.toList());

You could define a comparator of your type (I used E as a placeholder here) that will not change the order:
Comparator<E> NO_SORTING = (one, other) -> 0;
If the comparator field is an Optional of Comparator, you can then use
.sorted(comparator.orElse(NO_SORTING))

If you don't mind use third party library StreamEx
StreamEx(source).[...].chain(s -> comparator == null ? s : s.sorted(comparator)).[...];

You can accomplish this using an auxiliary function.
static <T, R> R applyFunction(T obj, Function<T, R> f) {
return f.apply(obj);
}
and
applyFunction([...].stream().map()[...],
stream -> comparator == null ? stream : stream.sorted(comparator))
[...];
You don't need to know intermediate stream type.

Related

Applying Pattern matching for Instanceof in the Stream

Suppose we have a Stream of Animals.
We have different Subclasses of Animals, and we want to apply a filter on the stream to only have the Zebras of the Stream. We now still have a Stream of Animals, but only containing Zebras. To get a stream of Zebras we still need to cast.
Stream<Zebra> zebraStream = animalStream
.filter(Zebra.class::isInstance)
.map(Zebra.class::cast);
Java 14 introduced pattern matching for instanceof, so we can now use:
if (animal instanceof Zebra zebra) {
System.out.println(zebra.countStripes());
}
Is there a way to use pattern matching in stream pipes?
Of course you could do something like this:
Stream<Zebra> zebraStream = animalStream.map(animal -> {
if (animal instanceof Zebra zebra) {
return zebra;
}
return null;
})
.filter(Objects::nonNull);
But IMHO this is really ugly.
Pattern matching + mapMulti
To coerce a Stream of supertype to a Stream of one of its subtypes, you can make use of the Pattern matching for instanceof in conjunction with Java 16 mapMulti(), which expects a stream element and a Consumer of the resulting type:
Stream<Animal> animalStream = Stream.of();
Stream<Zebra> zebraStream = animalStream
.mapMulti((animal, consumer) -> {
if (animal instanceof Zebra zebra) consumer.accept(zebra);
});
Pattern matching + flatMap
To use Pattern matching for instanceof you can also employ a classic stream operation flatMap(), which is like mapMulti() is meant to perform one-to-many transformations.
The important distinction between the two is that mapMulti() replace the initial stream element with zero or more elements via its Consumer, meanwhile flatMap() require a producing a new Stream to flatten the data. And in this case utilizing mapMulti() would be more advantages because if the list is large generating singleton-streams for every element might be costful.
Stream<Zebra> zebraStream = animalStream
.flatMap(animal ->
animal instanceof Zebra zebra ? Stream.of(zebra) : null
);
Note that according to the documentation instead of an empty stream, we can also return null (which is handy because Stream.empty() doesn't return constant but spawns a new object):
If a mapped stream is null an empty stream is used, instead.
I think you are almost done it! Just use filter instead of map:
Stream<Zebra> zebraStream = animalStream.stream()
.filter(animal -> animal instanceof Zebra)
.map(Zebra.class::cast);

convert java for loop into stream with filter

I have transformed a regular for loop code into java 8 streams. I tried a few, but i am still learning this and running out of ideas, please suggest ideas. can this be further simplified ? Other than using forEach, I am not able to change much.
Also, why do I have to typecast the eid to String in getERecordFromId((String)eid)
Stream <String>eIdsStream = getEidStream();
final HashSet<String> declinedRecords = new HashSet<>();
eIdsStream.forEach (eid -> {
ERecord eRecord = getERecordFromId((String)eid);
if(eRecord.getEHash() != null && Status.DECLINED == eRecord.getStatus()) {
declineRecords.add(eRecord.getEHash());
}
}
The casting is required since you use a raw Stream variable. Assuming getEidStream() returns a Stream<String>, you should have assigned it to a Stream<String> variable, or not assigned it to a variable at all.
Using forEach defeats the purpose of using Streams in the first place.
You should use filter and map to transform the Stream to hold the required elements, and then collect to a Set.
Set<String> declinedRecords =
getEidStream().map(eid -> getERecordFromId(eid))
.filter(eRecord -> eRecord.getEHash() != null && Status.DECLINED == eRecord.getStatus())
.map(ERecord::getEHash)
.collect(Collectors.toSet());

Java 8 filter by attribute

I have a class which is of the following definition
public class MyClass {
int val;
type t;
}
Where type is an enum with values A,B,C,D,....
I have a list of objects of MyClass and I want to filter out the first element of each type occurring in the list.
for example :-
Given list:
{{1,A},{2,A},{4,B},{5,B},{3,C}}
Output:
{{1,A},{4,B},{3,C}}
Is there a way to use filter() of a stream of the list to solve this problem?
I'm not sure if there's a way to do this with a single Stream pipeline, but you can do it with two.
The first pipeline groups the objects by the val property (producing a Map<Integer,List<MyClass>>) and the second takes the first object of each List produced by the first pipeline and collects them into the output List:
List<MyClass>
filtered = mycl.stream ()
.collect (Collectors.groupingBy (c -> c.val))
.values ()
.stream ()
.map (l -> l.get (0))
.collect (Collectors.toList ());
Here is a solution which is not as elegant I hoped for but it works:
Set<MyType> typeSet = new HashSet<>();
List<MyClass> result = list.stream()
.filter(c -> typeSet.add(c.getType())).collect(
Collectors.toList());
I'm not sure if there is any direct way of doing it but you can achieve it by doing
1) First use streams's findFirst method with filter (TypeOf type).
2) do above steps for all types.
3) Merge all above data into one list.
One of good way to achieve this override equals() and hashCode() in your MyClass class. Check equality on the basis of 'type'. Then put your List in Set it will remove all duplicate. :)

Java stream of optionals to optional stream

I need to convert Stream<Optional<Integer>> to Optional<Stream<Integer>>.
The output Optional<Stream<Integer>> should be an empty value when at least one value ofStream<Optional<Integer>> is empty.
Do you know any functional way to solve the problem? I tried to use collect method, but without success.
Well, the tricky thing here is that if you're just given a Stream, you can only use it once.
To be stateless and avoid redundant copying, one way is to just catch NoSuchElementException:
static <T> Optional<Stream<T>> invert(Stream<Optional<T>> stream) {
try {
return Optional.of(
stream.map(Optional::get)
.collect(Collectors.toList())
.stream());
} catch (NoSuchElementException e) {
return Optional.empty();
}
}
A simple inversion would be:
static <T> Optional<Stream<T>> invert(Stream<Optional<T>> stream) {
return Optional.of(stream.map(Optional::get));
}
But to find out if it contains an empty element, you need to actually traverse it which also consumes it.
If you're given the source of the stream, you can traverse it without collecting it:
static <T> Optional<Stream<T>> invert(
Supplier<Stream<Optional<T>>> supplier) {
// taking advantage of short-circuiting here
// instead of allMatch(Optional::isPresent)
return supplier.get().anyMatch(o -> !o.isPresent()) ?
Optional.empty() : Optional.of(supplier.get().map(Optional::get));
}
List<Optional<Integer>> myInts =
Arrays.asList(Optional.of(1), Optional.of(2), Optional.of(3));
Optional<Stream<Integer>> inverted = invert(myInts::stream);
That's probably a more interesting approach. (But it's prone to a race condition because the stream() is taken twice. If some other thread adds an empty element in between and gets away with it, we have a problem.)
Though this has already been answered yet to add to the list, with Java-9 introducing Optional.stream, this should be achievable as:
// initialized stream of optional
Stream<Optional<Integer>> so = Stream.empty();
// mapped stream of T
Stream<Integer> s = so.flatMap(Optional::stream);
// constructing optional from the stream
Optional<Stream<Integer>> os = Optional.of(s);
Similar to Radiodef's answer, though this one avoids the exception handling and the intermediate list.
private static <T> Optional<Stream<T>> invertOptional(Stream<Optional<T>> input) {
return input.map(integer -> integer.map(Stream::of))
.collect(Collectors.reducing((l, r) -> l.flatMap(lv -> r.map(rv -> Stream.concat(lv, rv)))))
.orElse(Optional.empty());
}
The way this works is it maps to a Stream of Optional Streams of T. The Optional.map is used in this case, so each one of the Optional<Stream<T>> items in the resultant stream is a either a Stream of 1, or an empty Optional.
Then it collects these streams by reducing them together. the l.flatMap will return an empty Optional if l is empty or the r.map returns an empty. if r.map isn't empty, it calls the Stream.concat, which combines the left and right stream values.
The whole collect reduction produces an Optional<Optional<Stream<T>>>, so we narrow that down with the .orElse(Optional.empty)
Note: Code is tested, and appears to work. The unspecified "edge case" of an empty input Stream is treated as an an empty Optional, but can be easily changed.
final Stream<Optional<Integer>> streamOfInts = Stream.of(Optional.of(1), Optional.of(2), Optional.of(3), Optional.of(4), Optional.of(5));
// false - list of Optional.empty(); true -> list of Optional.of(Integer)
final Map<Boolean, List<Optional<Integer>>> collect = streamOfInts.collect(Collectors.partitioningBy(Optional::isPresent));
final Function<List<Optional<Integer>>, Stream<Integer>> mapToStream = List->List.stream().filter(o->o.isPresent()).map(o->o.get());
Optional<Stream<Integer>> result = Optional
.of(Optional.of(collect.get(false)).filter(list->list.size()>0).orElse(collect.get(true)))
.filter(list->list.size()>0)
.filter(list->list.get(0).isPresent())
.map(mapToStream)
.map(Optional::of)
.orElse(Optional.empty());

Can you split a stream into two streams?

I have a data set represented by a Java 8 stream:
Stream<T> stream = ...;
I can see how to filter it to get a random subset - for example
Random r = new Random();
PrimitiveIterator.OfInt coin = r.ints(0, 2).iterator();
Stream<T> heads = stream.filter((x) -> (coin.nextInt() == 0));
I can also see how I could reduce this stream to get, for example, two lists representing two random halves of the data set, and then turn those back into streams.
But, is there a direct way to generate two streams from the initial one? Something like
(heads, tails) = stream.[some kind of split based on filter]
Thanks for any insight.
A collector can be used for this.
For two categories, use Collectors.partitioningBy() factory.
This will create a Map<Boolean, List>, and put items in one or the other list based on a Predicate.
Note: Since the stream needs to be consumed whole, this can't work on infinite streams. And because the stream is consumed anyway, this method simply puts them in Lists instead of making a new stream-with-memory. You can always stream those lists if you require streams as output.
Also, no need for the iterator, not even in the heads-only example you provided.
Binary splitting looks like this:
Random r = new Random();
Map<Boolean, List<String>> groups = stream
.collect(Collectors.partitioningBy(x -> r.nextBoolean()));
System.out.println(groups.get(false).size());
System.out.println(groups.get(true).size());
For more categories, use a Collectors.groupingBy() factory.
Map<Object, List<String>> groups = stream
.collect(Collectors.groupingBy(x -> r.nextInt(3)));
System.out.println(groups.get(0).size());
System.out.println(groups.get(1).size());
System.out.println(groups.get(2).size());
In case the streams are not Stream, but one of the primitive streams like IntStream, then this .collect(Collectors) method is not available. You'll have to do it the manual way without a collector factory. It's implementation looks like this:
[Example 2.0 since 2020-04-16]
IntStream intStream = IntStream.iterate(0, i -> i + 1).limit(100000).parallel();
IntPredicate predicate = ignored -> r.nextBoolean();
Map<Boolean, List<Integer>> groups = intStream.collect(
() -> Map.of(false, new ArrayList<>(100000),
true , new ArrayList<>(100000)),
(map, value) -> map.get(predicate.test(value)).add(value),
(map1, map2) -> {
map1.get(false).addAll(map2.get(false));
map1.get(true ).addAll(map2.get(true ));
});
In this example I initialize the ArrayLists with the full size of the initial collection (if this is known at all). This prevents resize events even in the worst-case scenario, but can potentially gobble up 2NT space (N = initial number of elements, T = number of threads). To trade-off space for speed, you can leave it out or use your best educated guess, like the expected highest number of elements in one partition (typically just over N/2 for a balanced split).
I hope I don't offend anyone by using a Java 9 method. For the Java 8 version, look at the edit history.
I stumbled across this question to my self and I feel that a forked stream has some use cases that could prove valid. I wrote the code below as a consumer so that it does not do anything but you could apply it to functions and anything else you might come across.
class PredicateSplitterConsumer<T> implements Consumer<T>
{
private Predicate<T> predicate;
private Consumer<T> positiveConsumer;
private Consumer<T> negativeConsumer;
public PredicateSplitterConsumer(Predicate<T> predicate, Consumer<T> positive, Consumer<T> negative)
{
this.predicate = predicate;
this.positiveConsumer = positive;
this.negativeConsumer = negative;
}
#Override
public void accept(T t)
{
if (predicate.test(t))
{
positiveConsumer.accept(t);
}
else
{
negativeConsumer.accept(t);
}
}
}
Now your code implementation could be something like this:
personsArray.forEach(
new PredicateSplitterConsumer<>(
person -> person.getDateOfBirth().isPresent(),
person -> System.out.println(person.getName()),
person -> System.out.println(person.getName() + " does not have Date of birth")));
Unfortunately, what you ask for is directly frowned upon in the JavaDoc of Stream:
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.
You can work around this using peek or other methods should you truly desire that type of behaviour. In this case, what you should do is instead of trying to back two streams from the same original Stream source with a forking filter, you would duplicate your stream and filter each of the duplicates appropriately.
However, you may wish to reconsider if a Stream is the appropriate structure for your use case.
You can get two Streams out of one
since Java 12 with teeing
counting heads and tails in 100 coin flips
Random r = new Random();
PrimitiveIterator.OfInt coin = r.ints(0, 2).iterator();
List<Long> list = Stream.iterate(0, i -> coin.nextInt())
.limit(100).collect(teeing(
filtering(i -> i == 1, counting()),
filtering(i -> i == 0, counting()),
(heads, tails) -> {
return(List.of(heads, tails));
}));
System.err.println("heads:" + list.get(0) + " tails:" + list.get(1));
gets eg.: heads:51 tails:49
Not exactly. You can't get two Streams out of one; this doesn't make sense -- how would you iterate over one without needing to generate the other at the same time? A stream can only be operated over once.
However, if you want to dump them into a list or something, you could do
stream.forEach((x) -> ((x == 0) ? heads : tails).add(x));
This is against the general mechanism of Stream. Say you can split Stream S0 to Sa and Sb like you wanted. Performing any terminal operation, say count(), on Sa will necessarily "consume" all elements in S0. Therefore Sb lost its data source.
Previously, Stream had a tee() method, I think, which duplicate a stream to two. It's removed now.
Stream has a peek() method though, you might be able to use it to achieve your requirements.
not exactly, but you may be able to accomplish what you need by invoking Collectors.groupingBy(). you create a new Collection, and can then instantiate streams on that new collection.
This was the least bad answer I could come up with.
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
public class Test {
public static <T, L, R> Pair<L, R> splitStream(Stream<T> inputStream, Predicate<T> predicate,
Function<Stream<T>, L> trueStreamProcessor, Function<Stream<T>, R> falseStreamProcessor) {
Map<Boolean, List<T>> partitioned = inputStream.collect(Collectors.partitioningBy(predicate));
L trueResult = trueStreamProcessor.apply(partitioned.get(Boolean.TRUE).stream());
R falseResult = falseStreamProcessor.apply(partitioned.get(Boolean.FALSE).stream());
return new ImmutablePair<L, R>(trueResult, falseResult);
}
public static void main(String[] args) {
Stream<Integer> stream = Stream.iterate(0, n -> n + 1).limit(10);
Pair<List<Integer>, String> results = splitStream(stream,
n -> n > 5,
s -> s.filter(n -> n % 2 == 0).collect(Collectors.toList()),
s -> s.map(n -> n.toString()).collect(Collectors.joining("|")));
System.out.println(results);
}
}
This takes a stream of integers and splits them at 5. For those greater than 5 it filters only even numbers and puts them in a list. For the rest it joins them with |.
outputs:
([6, 8],0|1|2|3|4|5)
Its not ideal as it collects everything into intermediary collections breaking the stream (and has too many arguments!)
I stumbled across this question while looking for a way to filter certain elements out of a stream and log them as errors. So I did not really need to split the stream so much as attach a premature terminating action to a predicate with unobtrusive syntax. This is what I came up with:
public class MyProcess {
/* Return a Predicate that performs a bail-out action on non-matching items. */
private static <T> Predicate<T> withAltAction(Predicate<T> pred, Consumer<T> altAction) {
return x -> {
if (pred.test(x)) {
return true;
}
altAction.accept(x);
return false;
};
/* Example usage in non-trivial pipeline */
public void processItems(Stream<Item> stream) {
stream.filter(Objects::nonNull)
.peek(this::logItem)
.map(Item::getSubItems)
.filter(withAltAction(SubItem::isValid,
i -> logError(i, "Invalid")))
.peek(this::logSubItem)
.filter(withAltAction(i -> i.size() > 10,
i -> logError(i, "Too large")))
.map(SubItem::toDisplayItem)
.forEach(this::display);
}
}
Shorter version that uses Lombok
import java.util.function.Consumer;
import java.util.function.Predicate;
import lombok.RequiredArgsConstructor;
/**
* Forks a Stream using a Predicate into postive and negative outcomes.
*/
#RequiredArgsConstructor
#FieldDefaults(makeFinal = true, level = AccessLevel.PROTECTED)
public class StreamForkerUtil<T> implements Consumer<T> {
Predicate<T> predicate;
Consumer<T> positiveConsumer;
Consumer<T> negativeConsumer;
#Override
public void accept(T t) {
(predicate.test(t) ? positiveConsumer : negativeConsumer).accept(t);
}
}
How about:
Supplier<Stream<Integer>> randomIntsStreamSupplier =
() -> (new Random()).ints(0, 2).boxed();
Stream<Integer> tails =
randomIntsStreamSupplier.get().filter(x->x.equals(0));
Stream<Integer> heads =
randomIntsStreamSupplier.get().filter(x->x.equals(1));

Categories

Resources