如何在Java流上应用多个过滤器?

如何在Java流上应用多个过滤器?,java,java-stream,Java,Java Stream,我必须通过一个映射过滤对象集合,该映射保存对象字段名和字段值的键值对。我正在尝试按流()应用所有筛选器。筛选器() 这些对象实际上是JSON,因此映射包含其变量的名称以及它们必须包含的值,以便被接受,但出于简单的原因,并且由于与问题无关,我编写了一个简单的Testclass来模拟该行为: public class TestObject { private int property1; private int property2; private int property3;

我必须通过一个映射过滤对象集合,该映射保存对象字段名和字段值的键值对。我正在尝试按流()应用所有筛选器。筛选器()

这些对象实际上是JSON,因此映射包含其变量的名称以及它们必须包含的值,以便被接受,但出于简单的原因,并且由于与问题无关,我编写了一个简单的Testclass来模拟该行为:

public class TestObject {

  private int property1;
  private int property2;
  private int property3;

  public TestObject(int property1, int property2, int property3) {
      this.property1 = property1;
      this.property2 = property2;
      this.property3 = property3;
  }

  public int getProperty(int key) {
      switch(key) {
          case 1: return property1;
          case 2: return property2;
          default: return property3;
      }
  }
}
到目前为止,我所尝试的:

public static void main(String[] args) {
    List<TestObject> list = new ArrayList<>();
    Map<Integer, Integer> filterMap = new HashMap<>();
    list.add(new TestObject(1, 2, 3));
    list.add(new TestObject(1, 2, 4));
    list.add(new TestObject(1, 4, 3));
    filterMap.put(3, 3); //Filter property3 == 3
    filterMap.put(2, 2); //Filter property2 == 2

    //Does not apply the result
    filterMap.forEach((key, value) -> list.stream()
            .filter(testObject -> testObject.getProperty(key) == value)
            .collect(Collectors.toList())
    );
    /* Gives error: boolean can not be converted to void
    list = list.stream()
            .filter(testObject -> filterMap.forEach((key, value) -> testObject.getProperty(key) == value))
            .collect(Collectors.toList()
            );
    */
    //Printing result

    list.forEach(obj -> System.out.println(obj.getProperty(1) + " " + obj.getProperty(2) + " " + obj.getProperty(3)));
}
编辑:


Sweeper在他的回答中很好地总结了我的问题,所以这里再次澄清一下(可能是未来的读者):我想保留满足所有过滤器的所有对象。

我想您想保留满足映射指定的所有条件的所有
测试对象

这将完成以下工作:

List<TestObject> newList = list.stream()
        .filter(x ->
                filterMap.entrySet().stream()
                        .allMatch(y ->
                                x.getProperty(y.getKey()) == y.getValue()
                        )
        )
        .collect(Collectors.toList());
List newList=List.stream()
.过滤器(x->
filterMap.entrySet().stream()
.allMatch(y->
x、 getProperty(y.getKey())==y.getValue()
)
)
.collect(Collectors.toList());
翻译成“英语”

filter
通过保留所有元素
x
来过滤列表
list

  • filterMap
    的所有键值对
    y
    必须满足:
    • x.getProperty(y.getKey())==y.getValue()

(我不认为我能很好地让这篇文章更具可读性……)如果你想要一个更具可读性的解决方案,我推荐Jeroen Steenbeeke的答案。

要对流应用数量可变的过滤步骤(只有在运行时才知道),你可以使用一个循环来添加过滤步骤

Stream<TestObject> stream = list.stream();
for (Predicate<TestObject> predicate: allPredicates) {
  stream = stream.filter(predicate);
}
list = stream.collect(Collectors.toList());
Stream=list.Stream();
for(谓词:所有谓词){
stream=stream.filter(谓词);
}
list=stream.collect(Collectors.toList());

它与过滤器无关。实际上,过滤器永远不会按照您的代码工作。看

//Does not apply the result
filterMap.forEach((key, value) -> list.stream()
        .filter(testObject -> testObject.getProperty(key) == value)
        .collect(Collectors.toList())
);
列表已筛选,但此处未更改任何内容。未删除任何元素,也未更改任何对象地址。尝试
removeIf

// Does not apply the result
filterMap.forEach((key, value) -> list.removeIf(testObject -> testObject.getProperty(key) != value));
输出为

1 2 3

一种更通用的方法是创建一个多过滤器(
谓词
),该过滤器使用
谓词和(…)
谓词或(…)
连接。这适用于任何使用
谓词
——首先是
可选

由于结果是一个
谓词
本身,因此可以继续使用
谓词和(…)
谓词。或(…)
或再次使用
多谓词
构建更复杂的谓词

class MultiPredicate {

    public static <T> Predicate<T> matchingAll(Collection<Predicate<T>> predicates) {
        Predicate<T> multiPredicate = to -> true;
        for (Predicate<T> predicate : predicates) {
            multiPredicate = multiPredicate.and(predicate);
        }

        return multiPredicate;    
    }

    @SafeVarargs
    public static <T> Predicate<T> matchingAll(Predicate<T> first, Predicate<T>... other) {
        if (other == null || other.length == 0) {
            return first;
        }

        Predicate<T> multiPredicate = first;
        for (Predicate<T> predicate : other) {
            multiPredicate = multiPredicate.and(predicate);
        }

        return multiPredicate;
    }

    public static <T> Predicate<T> matchingAny(Collection<Predicate<T>> predicates) {
        Predicate<T> multiPredicate = to -> false;
        for (Predicate<T> predicate : predicates) {
            multiPredicate = multiPredicate.or(predicate);
        }

        return multiPredicate;    
    }

    @SafeVarargs
    public static <T> Predicate<T> matchingAny(Predicate<T> first, Predicate<T>... other) {
        if (other == null || other.length == 0) {
            return first;
        }

        Predicate<T> multiPredicate = first;
        for (Predicate<T> predicate : other) {
            multiPredicate = multiPredicate.or(predicate);
        }

        return multiPredicate;
    }

}
如果我们使用
MultiPredicate.matchingAny(…)
结果是:

(1|2|3)  
(1|2|3)  
(1|2|4)  
(1|4|3)  

请更具体地说,你希望得到什么结果我读你的答案没有问题,我真的很喜欢,非常感谢!)您不应该使用
==
来比较
整数
s。@Michael
getProperty
返回一个原语
int
,因此右侧将被解锁,对吗?@Sweeper一般来说,您的代码比Jeroen Steenbeeke的效率更高(性能更好)s@user_3380739一如既往,这样的性能预测是有风险的——这当然取决于是否有更多的过滤器或更多的对象需要测试。这种方法为
列表中的每个条目创建一个
——实例,每个过滤器创建一个实例。profile可能很有趣。那么.filter()是什么呢?
(1|2|3)  
(1|2|3)  
(1|2|4)  
(1|4|3)