When you have a Stream of Objects you can filter on them pretty elegantly.
swimmingAnimalStream = animalStream
.filter(Animal::canSwim);
When you have slightly more complex filters instead of using Method references you have to use Lambdas.
greenAnimals = animalStream
.filter(animal -> animal.getColor().equals(Colors.GREEN));
Is there a way to map the value before filtering on it, but still have the complete object after the filter? So the fallowing is not what I want:
animalStream
.map(Animal::getColor)
.filter(Colors.GREEN::equals)
With this I would be left with color information only. What I also would like to avoid is extracting the method. I am looking for a more streamlined way of doing this. Something like this for example:
animalStream
.filter(Colors.GREEN::equals, Animal::getColor);
The method signature of this filter method would look like this.
<MAPPED> Stream<T> filter(Predicate<MAPPED> filter, Function<? super T, MAPPED> mappingFunction);
Even better would be a version where you could join multiple mapping functions. On the fly one could maybe use a varargs for the mappingFunction. But I honestly don’t know how that would be possible with Generics. But that’s a different story.
The solution should also be able to use whatever Predicate that one could imagine. Equals is just an Example. Another example would be to check if a field from the object is present.
animalWithMotherStream = animalStream
.filter(Optional::isPresent, Animal::getMother);
Does anyone now a cleaner Solution, or a library that does this already?
CodePudding user response:
StreamEx, a library that provides extended stream methods and classes, has filterBy
:
public <K> StreamEx<T> filterBy(Function<? super T,? extends K> mapper, K value)
Returns a stream consisting of the elements of this stream for which the supplied mapper function returns the given value.
This method behaves like
filter(t -> Objects.equals(value, mapper.apply(t)))
.
CodePudding user response:
filter
accepts a Predicate, whose function can only return a boolean. There's no other method signature.
If you want to filter by all green animals, you'd use
animalStream
.filter(a -> Colors.GREEN.equals(a.getColor()))
or
Predicate<Animal> isGreen = (a) -> Colors.GREEN.equals(a.getColor());
Stream<Animal> greenAnimals = animalStream.filter(isGreen);
Don't use map
unless you want a Stream<COLOR>
join multiple mapping functions
You can chain them, rather than join - .stream().map().map()
, but as you discovered, this does not preserve the original type.