[英]Stream map on filter
当您有 Stream 个对象时,您可以非常优雅地过滤它们。
swimmingAnimalStream = animalStream
.filter(Animal::canSwim);
当您有稍微复杂的过滤器而不是使用方法引用时,您必须使用 Lambdas。
greenAnimals = animalStream
.filter(animal -> animal.getColor().equals(Colors.GREEN));
有没有办法把过滤前的值map放在上面,但过滤后仍然有完整的object? 所以休闲不是我想要的:
animalStream
.map(Animal::getColor)
.filter(Colors.GREEN::equals)
有了这个,我将只剩下颜色信息。 我还想避免提取方法。 我正在寻找一种更简化的方法来做到这一点。 例如这样的事情:
animalStream
.filter(Colors.GREEN::equals, Animal::getColor);
此过滤器方法的方法签名如下所示。
<MAPPED> Stream<T> filter(Predicate<MAPPED> filter, Function<? super T, MAPPED> mappingFunction);
更好的是你可以加入多个映射函数的版本。 在运行过程中,可以对 mappingFunction 使用可变参数。 但老实说,我不知道 Generics 怎么可能。但那是另一回事了。
该解决方案还应该能够使用人们可以想象的任何谓词。 等于只是一个例子。 另一个示例是检查 object 中的字段是否存在。
animalWithMotherStream = animalStream
.filter(Optional::isPresent, Animal::getMother);
现在有没有人提供更清洁的解决方案或已经这样做的库?
是的,从 Java 16 开始,有一个使用Stream#mapMulti
的解决方案。
animalStream
.mapMulti((animal, consumer) -> {
if (Colors.GREEN.equals(animal.getColor())) { // condition
consumer.accept(animal); // qualify the instance
}
})
... // further operations
提供的Consumer<R>
接受根据您的标准合格的实例。
优点:这种命令式方法在性能方面的优势在于,您不必调用两个Stream
操作,而只需调用一个操作,例如,可以替换Stream#map
和Stream#filter
的组合。 虽然最大的优点是Consumer#accept
可以根据需要调用多次,因此您可以有效地增加Stream
中的条目数。
缺点:但是,您丢失了一些声明性方法,如果仅在片段中使用 as 而无需进一步处理,则值得使用相当简单的 for 循环或坚持使用filter
操作(见下文):
只需将条件写到Stream#filter
,这是正确的 Stream 用法:
animalStream
.filter(animal -> Colors.GREEN.equals(animal.getColor()))
...
filter
接受一个 Predicate,它的 function 只能返回一个 boolean。 没有其他方法签名。
如果你想过滤所有绿色动物,你会使用
animalStream
.filter(a -> Colors.GREEN.equals(a.getColor()))
或者
Predicate<Animal> isGreen = (a) -> Colors.GREEN.equals(a.getColor());
Stream<Animal> greenAnimals = animalStream.filter(isGreen);
不要使用map
除非你想要一个Stream<COLOR>
加入多个映射函数
您可以链接它们,而不是加入 - .stream().map().map()
,但正如您发现的那样,这不会保留原始类型。
您可以使用 Guava 的Predicates.compose()
,或创建您自己的:
public static <A, B> Predicate<A> compose(
Predicate<B> predicate, Function<A, ? extends B> function) {
return a -> predicate.test(function.apply(a));
}
现在只需将其传递到您的过滤器中:
animalStream.filter(compose(Colors.GREEN::equals, Animal::getColor))
至于可变参数概念,我怀疑这在 Java 的 generics 下是否可行,除非它们都是同一类型,在这种情况下,您只需在一个循环中apply()
或使用Function.andThen()
减少它们。
操作Stream.filter()
需要一个Predicate
,它是一个function产生一个boolean
结果。 这个filter
的定义非常直观和自成一体,没有其他风格的过滤器(我怀疑它们是否会在未来出现)。
但是,您可以创建Predicate
的实现并为其提供所需的所有行为。 作为 Predicate,它将有资格在filter
中使用。
在介绍实现之前,我将展示一些可以赋予此类自定义Predicate
的功能。
Stream<Animal> greenAnimals = animalStream
.filter(
MultiPred.ofOr(Animal::getColor, Color.WHITE::equals, Color.GREEN::equals)
.or(
MultiPred.of(Animal::getType, AnimalType.CARNIVORE::equals)
.and(Animal::canFly) // we can chain custom Predicates with regular ones
)
.or(
MultiPred.of(Animal::getType, AnimalType.HERBIVORE::equals)
.and(MultiPred.of(Animal::getColor, Color.PURPLE::equals)
.or(Animal::canSwim)
)
)
);
这是上面示例中使用的虚拟 class Animal
和枚举:
public class Animal {
private AnimalType type;
private boolean canSwim;
private boolean canFly;
private Color color;
// getters
}
public enum AnimalType {
CARNIVORE, HERBIVORE
}
public enum Color {
GREEN, WHITE, PURPLE
}
您可以提供具有您需要的任何功能的自定义Predicate
。
以下谓词期望公开方法,期望keyExtractor function 和一个谓词,或者一组必须用逻辑或||
链接的谓词 , 或逻辑与&&
。
public class MultiPred<T, K> implements Predicate<T> {
private final BiFunction<Function<T, K>, Predicate<K>, Predicate<T>>
predicateProducer = (f, p) -> t -> p.test(f.apply(t));
private final Predicate<T> p;
private MultiPred(Function<T, K> keyExtractor,
Predicate<K> predicate) {
this.p = predicateProducer.apply(keyExtractor, predicate);
}
@SafeVarargs
public static <T, K> MultiPred<T, K> ofAnd(Function<T, K> keyExtractor,
Predicate<K>... predicates) {
return of(keyExtractor, k -> true, Predicate::and, predicates);
}
@SafeVarargs
public static <T, K> MultiPred<T, K> ofOr(Function<T, K> keyExtractor,
Predicate<K>... predicates) {
return of(keyExtractor, k -> false, Predicate::or, predicates);
}
@SafeVarargs
public static <T, K> MultiPred<T, K> of(Function<T, K> keyExtractor,
Predicate<K> identity,
BinaryOperator<Predicate<K>> op,
Predicate<K>... predicates) {
Objects.requireNonNull(predicates);
Predicate<K> predicate = Arrays.stream(predicates).reduce(identity, op);
return new MultiPred<>(keyExtractor, predicate);
}
public static <T, K> MultiPred<T, K> of(Function<T, K> keyExtractor,
Predicate<K> predicate) {
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(predicate);
return new MultiPred<>(keyExtractor, predicate);
}
@Override
public boolean test(T t) {
Objects.requireNonNull(t);
return p.test(t);
}
@Override
public Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return p.and(other);
}
@Override
public Predicate<T> negate() {
return p.negate();
}
@Override
public Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return p.or(other);
}
}
上面 class 的逻辑可以具体化为实用程序 class 公开一组static
方法来生成Predicate
s。 它将保留开头使用示例中显示的所有容量。
(这个想法的功劳属于@Holger 和@shmosel,因为他发布了生成谓词的 static 方法,因此下面显示的代码应该被认为是建立在@shmosel 的答案之上的)
public static class MultiPred {
private MultiPred() {}
@SafeVarargs
public static <T, K> Predicate<T> ofAnd(Function<T, K> keyExtractor,
Predicate<K>... predicates) {
return of(keyExtractor, k -> true, Predicate::and, predicates);
}
@SafeVarargs
public static <T, K> Predicate<T> ofOr(Function<T, K> keyExtractor,
Predicate<K>... predicates) {
return of(keyExtractor, k -> false, Predicate::or, predicates);
}
@SafeVarargs
public static <T, K> Predicate<T> of(Function<T, K> keyExtractor,
Predicate<K> identity,
BinaryOperator<Predicate<K>> op,
Predicate<K>... predicates) {
Objects.requireNonNull(predicates);
Predicate<K> predicate = Arrays.stream(predicates).reduce(identity, op);
return getPredicateProducer(keyExtractor, predicate);
}
public static <T, K> Predicate<T> of(Function<T, K> keyExtractor,
Predicate<K> predicate) {
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(predicate);
return getPredicateProducer(keyExtractor, predicate);
}
private static <T, K> Predicate<T> getPredicateProducer(Function<T, K> keyExtractor,
Predicate<K> predicate) {
return t -> predicate.test(keyExtractor.apply(t));
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.