繁体   English   中英

过滤器上的 Stream map

[英]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

是的,从 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#mapStream#filter的组合。 虽然最大的优点是Consumer#accept可以根据需要调用多次,因此您可以有效地增加Stream中的条目数。

缺点:但是,您丢失了一些声明性方法,如果仅在片段中使用 as 而无需进一步处理,则值得使用相当简单的 for 循环或坚持使用filter操作(见下文):

较旧的 Java 版本

只需将条件写到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() ,但正如您发现的那样,这不会保留原始类型。

StreamEx是一个提供扩展的 stream 方法和类的库,具有filterBy

 public <K> StreamEx<T> filterBy(Function<? super T,? extends K> mapper, K value)

返回由 stream 的元素组成的 stream,提供的映射器 function 返回给定值。

此方法的行为类似于filter(t -> Objects.equals(value, mapper.apply(t)))

您可以使用 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 的逻辑可以具体化为实用程序 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM