根据独立谓词流式传输集合并收集到多个结果

我正在盯着一些命令式的代码,我试图将其转换为纯粹的function性风格。 基本上在inputSet有一个迭代for循环,我在其中检查3个谓词并根据哪个谓词匹配填充3个outputSets 。 输出集可以重叠。 如何使用Java 8流/ map / filter /等以纯函数方式执行此操作?

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

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

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

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

这里结果列表中的第一个对应于第一个谓词,依此类推。

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

 public static  Collector> multiClassify( List> predicates, Collector downstream) { Supplier dsSupplier = downstream.supplier(); BiConsumer dsAccumulator = downstream.accumulator(); BinaryOperator dsCombiner = downstream.combiner(); Supplier> supplier = () -> Stream.generate(dsSupplier) .limit(predicates.size()).collect(Collectors.toList()); BiConsumer, 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> 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> result = (Collector>) (Collector) Collector.of(supplier, accumulator, combiner, dsCharacteristics); return result; } Function dsFinisher = downstream.finisher(); Function, List> finisher = l -> l.stream().map(dsFinisher) .collect(Collectors.toList()); return Collector.of(supplier, accumulator, combiner, finisher, dsCharacteristics); } 

它接受一个谓词列表,并返回每个谓词的下游收集器结果列表。 用法示例:

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

另一种方法是使用Consumer.andThen(anotherConsumer)方法来创建由内部使用者组成的组合使用者,这些使用者按顺序执行。 这些内部消费者中的每一个都将测试每个谓词并根据它们是否匹配来对元素进行分类。

 public static  Consumer classify(Predicate predicate, Consumer action) { return elem -> Stream.of(elem) .filter(predicate) .findAny() .ifPresent(action); } 

只要谓词对元素返回true ,此实用程序方法将返回将对正在使用的元素执行给定操作的使用者。

测试:

 Stream stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); Set set1 = new LinkedHashSet<>(); Set set2 = new LinkedHashSet<>(); Set set3 = new LinkedHashSet<>(); // Here's the composed consumer, made of inner consumers Consumer multiClassifier = classify(n -> n % 2 == 0, set1::add) .andThen(classify(n -> n % 3 == 0, set2::add)) .andThen(classify(n -> n % 5 == 0, set3::add)); // Here the stream is consumed by the composed consumer stream.forEach(multiClassifier); 

每个内部使用者都是使用上面定义的实用程序方法创建的,它接收一个独立的谓词,当匹配时,它会将流的元素添加到给定的集合中,即如果流的元素是3的倍数,它将是添加到set2

最后,流与此组合的使用者一起使用,因此流由独立谓词分类:

 System.out.println(set1); // [2, 4, 6, 8, 10, 12] System.out.println(set2); // [3, 6, 9, 12] System.out.println(set3); // [5, 10]