簡體   English   中英

如何將多個謂詞應用於java.util.Stream?

[英]How to apply multiple predicates to a java.util.Stream?

如何將多個謂詞應用於java.util.Stream's filter()方法?

這就是我現在所做的,但我並不喜歡它。 Collection了一些東西,我需要根據過濾器Collection (謂詞)減少事物的數量:

Collection<Thing> things = someGenerator.someMethod();
List<Thing> filtered = things.parallelStream().filter(p -> {
   for (Filter f : filtersCollection) {
      if (f.test(p))
        return true;
   }
   return false;
}).collect(Collectors.toList());

我知道如果我事先了解過濾器的數量,我可以這樣做:

List<Thing> filtered = things.parallelStream().filter(filter1).or(filter2).or(filter3)).collect(Collectors.toList());

但是如何在不混合編程風格的情況下應用未知數量的謂詞? 知道它看起來有點難看......

如果你有一個Collection<Predicate<T>> filters你總是可以使用名為reduction的過程從中創建一個謂詞:

Predicate<T> pred=filters.stream().reduce(Predicate::and).orElse(x->true);

要么

Predicate<T> pred=filters.stream().reduce(Predicate::or).orElse(x->false);

取決於您希望如何組合過濾器。

如果指定一個空的謂詞集合后備orElse呼叫滿足身份角色( x->true做了and荷蘭國際集團的謂詞和x->false確實為or荷蘭國際集團),你也可以使用reduce(x->true, Predicate::and)reduce(x->false, Predicate::or)來獲取過濾器但是對於非常小的集合效率稍低,因為它總是將標識謂詞與集合的謂詞組合在一起,即使它只包含一個謂詞。 相反,如果集合的大小為1則上面顯示的變量reduce(accumulator).orElse(fallback)將返回單個謂詞。


注意這種模式如何也適用於類似的問題:擁有Collection<Consumer<T>>你可以使用創建一個Consumer<T>

Consumer<T> c=consumers.stream().reduce(Consumer::andThen).orElse(x->{});

等等。

我假設你的Filter是一個與java.util.function.Predicate不同的類型,這意味着它需要適應它。 一種可行的方法是這樣的:

things.stream().filter(t -> filtersCollection.stream().anyMatch(f -> f.test(t)));

這會為每個謂詞評估重新創建過濾器流帶來輕微的性能損失。 為了避免這種情況,你可以將每個過濾器包裝成一個Predicate並組成它們:

things.stream().filter(filtersCollection.stream().<Predicate>map(f -> f::test)
                       .reduce(Predicate::or).orElse(t->false));

但是,由於現在每個過濾器都落后於它自己的Predicate ,引入了一個更多的間接層,因此哪種方法具有更好的整體性能並不明確。

如果沒有適應性問題(如果你的Filter碰巧是一個Predicate ),問題陳述會變得更加簡單,第二種方法顯然勝出:

things.stream().filter(
   filtersCollection.stream().reduce(Predicate::or).orElse(t->true)
);

我設法解決了這樣一個問題,如果用戶想要在一個過濾操作中應用一個謂詞列表,一個可以是動態但沒有給出的列表,應該減少為一個謂詞 - 如下所示:

public class TestPredicates {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println(numbers.stream()
                .filter(combineFilters(x -> x > 2, x -> x < 9, x -> x % 2 == 1))
                .collect(Collectors.toList()));
    }

    public static <T> Predicate<T> combineFilters(Predicate<T>... predicates) {

        Predicate<T> p = Stream.of(predicates).reduce(x -> true, Predicate::and);
        return p;

    }
}

請注意,這將它們與“AND”邏輯運算符組合在一起。 要與“OR”組合,reduce行應該是:

Predicate<T> p = Stream.of(predicates).reduce(x -> false, Predicate::or);

這是一種解決這個問題的有趣方法,(直接粘貼於http://www.leveluplunch.com/java/tutorials/006-how-to-filter-arraylist-stream-java8/ )。 我認為這是一種更有效的方式。

Predicate<BBTeam> nonNullPredicate = Objects::nonNull;
Predicate<BBTeam> nameNotNull = p -> p.teamName != null;
Predicate<BBTeam> teamWIPredicate = p -> p.teamName.equals("Wisconsin");

Predicate<BBTeam> fullPredicate = nonNullPredicate.and(nameNotNull)
        .and(teamWIPredicate);

List<BBTeam> teams2 = teams.stream().filter(fullPredicate)
        .collect(Collectors.toList());

編輯:這是如何處理謂詞ToIgnore是謂詞列表的循環。 我從中創建了一個謂詞predicateToIgnore。

Predicate<T> predicateToIgnore = null;
for (Predicate<T> predicate : predicatesToIgnore) {
    predicateToIgnore = predicateToIgnore == null ? predicate : predicateToIgnore.or(predicate);
}

然后,使用此單個謂詞執行過濾。 這創建了更好的過濾器恕我直言

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM