根据独立谓词将流收集并收集到多个结果中 [英] Stream a collection and collect into multiple results depending on independent predicates

查看:107
本文介绍了根据独立谓词将流收集并收集到多个结果中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在盯着一些我试图转换为纯功能风格的命令式代码。
基本上有一个遍历 inputSet 的迭代循环,其中我检查3个谓词并填充3 outputSets 取决于哪些谓词匹配。输出集可以重叠。我怎样才能以纯功能的方式使用Java 8 streams / map / filter / etc?来完成这项工作? 解决方案

最简单的解决方案(除了保留所有内容外,更容易)创建三个独立的流:

  Set< MyObj> set1 = inputSet.stream()。filter(pred1).collect(Collectors.toSet()); 
设定< MyObj> set2 = inputSet.stream()。filter(pred2).collect(Collectors.toSet());
设定< MyObj> set3 = inputSet.stream()。filter(pred3).collect(Collectors.toSet());

如果您有谓词列表,则可以创建相应的集合列表: / p>

  List< Predicate< MyObj>>谓词= Arrays.asList(pred1,pred2,pred3); 
列表< Set< MyObj>> result = predicates.stream()
.map(pred - > inputSet.stream()。filter(pred).collect(Collectors.toSet()))
.collect(Collectors.toList() );

这里,结果列表中的第一组对应于第一个谓词等等。



如果您真的想单次处理您的输入(无论出于何种原因),您可以为此编写一个特殊的收集器。下面是一个非常普遍的例子:

  public static< T,A,R>收集器< T,?,List>> multiClassify(
List< Predicate< T>>谓词,Collector< super T,A,R>下游){
Supplier< A> dsSupplier = downstream.supplier();
BiConsumer< A,?超T> dsAccumulator = downstream.accumulator();
BinaryOperator< A> dsCombiner = downstream.combiner();

供应商<列表< A>> supplier =() - > Stream.generate(dsSupplier)
.limit(predicates.size())。collect(Collectors.toList());

BiConsumer< List< A> ;, T> accumulator =(list,t) - > IntStream
.range(0,predicates.size())。filter(i - > predicates.get(i).test(t))
.forEach(i - > dsAccumulator.accept list.get(i),t));

BinaryOperator< List< A>>组合器=(l1,l2) - > IntStream.range(0,predicates.size())
.mapToObj(i - > dsCombiner.apply(l1.get(i),l2.get(i)))
.collect .toList());

特征[] dsCharacteristics = downstream.characteristics()。toArray(
new Characteristics [0]);
if(downstream.characteristics()。contains(Characteristics.IDENTITY_FINISH)){
@SuppressWarnings(unchecked)
收集器< T,?,List< R>> (收集器< T,?,List>>)(收集器< T,?,?>)
Collector.of(supplier,accumulator,combiner,dsCharacteristics);
返回结果;
}
函数< A,R> dsFinisher = downstream.finisher();
函数<列表< A>,列表< R>>整理器= 1 - > l.stream()。map(dsFinisher)
.collect(Collectors.toList());
return Collector.of(supplier,accumulator,combiner,finisher,dsCharacteristics);





$ b

它为每个谓词获取下游收集器结果的谓词和返回列表。用法示例:

 列表< String> input = asList(abc,ade,bcd,cc,cdac); 

List< Predicate< String>> preds = asList(
s - > s.length()== 3,
s - > s.startsWith(a),
s - > s.endsWith(c ));
列表< Set< String>> result = input.stream()。collect(multiClassify(preds,Collectors.toSet()));
// [[bcd,abc,ade],[abc,ade],[cc,abc,cdac]]


I am staring at some imperative code which I am trying to convert to a purely functional style. Basically there is a iterative for-loop over inputSet in which I check 3 predicates and fill 3 outputSets depending on which predicate matches. The output sets can be overlapping. How can I do this in a pure functional way using Java 8 streams/map/filter/ etc.?

解决方案

The easiest solution (except leaving everything as is which is even easier) is to create three separate streams:

Set<MyObj> set1 = inputSet.stream().filter(pred1).collect(Collectors.toSet());
Set<MyObj> set2 = inputSet.stream().filter(pred2).collect(Collectors.toSet());
Set<MyObj> set3 = inputSet.stream().filter(pred3).collect(Collectors.toSet());

If you have a List of predicates, you can create a corresponding List of sets as a result:

List<Predicate<MyObj>> predicates = Arrays.asList(pred1, pred2, pred3);
List<Set<MyObj>> result = predicates.stream()
        .map(pred -> inputSet.stream().filter(pred).collect(Collectors.toSet()))
        .collect(Collectors.toList());

Here the first set in the resulting list corresponds to the first predicate and so on.

If you really want to process your input in single pass (for whatever reason), you may write a special collector for this. Here's one which is quite universal:

public static <T, A, R> Collector<T, ?, List<R>> multiClassify(
        List<Predicate<T>> predicates, Collector<? super T, A, R> downstream) {
    Supplier<A> dsSupplier = downstream.supplier();
    BiConsumer<A, ? super T> dsAccumulator = downstream.accumulator();
    BinaryOperator<A> dsCombiner = downstream.combiner();

    Supplier<List<A>> supplier = () -> Stream.generate(dsSupplier)
            .limit(predicates.size()).collect(Collectors.toList());

    BiConsumer<List<A>, T> accumulator = (list, t) -> IntStream
            .range(0, predicates.size()).filter(i -> predicates.get(i).test(t))
            .forEach(i -> dsAccumulator.accept(list.get(i), t));

    BinaryOperator<List<A>> combiner = (l1, l2) -> IntStream.range(0, predicates.size())
            .mapToObj(i -> dsCombiner.apply(l1.get(i), l2.get(i)))
            .collect(Collectors.toList());

    Characteristics[] dsCharacteristics = downstream.characteristics().toArray(
            new Characteristics[0]);
    if (downstream.characteristics().contains(Characteristics.IDENTITY_FINISH)) {
        @SuppressWarnings("unchecked")
        Collector<T, ?, List<R>> result = (Collector<T, ?, List<R>>) (Collector<T, ?, ?>) 
            Collector.of(supplier, accumulator, combiner, dsCharacteristics);
        return result;
    }
    Function<A, R> dsFinisher = downstream.finisher();
    Function<List<A>, List<R>> finisher = l -> l.stream().map(dsFinisher)
           .collect(Collectors.toList());
    return Collector.of(supplier, accumulator, combiner, finisher, dsCharacteristics);
}

It takes a list of predicates and returns list of downstream collector results for each predicate. Usage example:

List<String> input = asList("abc", "ade", "bcd", "cc", "cdac");

List<Predicate<String>> preds = asList(
        s -> s.length() == 3, 
        s -> s.startsWith("a"), 
        s -> s.endsWith("c"));
List<Set<String>> result = input.stream().collect(multiClassify(preds, Collectors.toSet()));
// [[bcd, abc, ade], [abc, ade], [cc, abc, cdac]]

这篇关于根据独立谓词将流收集并收集到多个结果中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆