2017-04-21 68 views
2

我正在寻找一个选项来过滤插入流但使用优先级。优先考虑Java中的流过滤器函数

以下为伪代码:

results.stream().filter(prio1).ifNotFound(filter(prio2)).collect(toList()) 

结果的列表,由第一标准被过滤称为“prio1”,如果不存在没有找到匹配第二滤波器应适用于尝试滤波器在第二个标准被称为prio2,然后结果将被收集

如何在Java 8中使用流实现此?

我正在寻找流中的一行。

+0

目前还不清楚你的意思是'ifNotFound'。 –

+0

你可以说第二个过滤器prio2不会被应用在哪种情况下? – MigSena

+0

所有项目应与prio1首先匹配,如果找不到,则再次将所有项目匹配到prio2 – Ckkn

回答

3

您需要stream()你的结果的两倍,但下面应该工作作为一个班轮:

results.stream().filter(results.stream().anyMatch(prio1) ? prio1 : prio2).collect(Collectors.toList()); 

(感谢flakes的第一个发布使用类似的策略多衬垫。 )

编辑:由于一些优秀的新的答案已经水落石出,我以为我会提供这种多数据流的短期防御/ anyMatch战略出不来克参考此主题的某些其他部分:

  • As pointed out by eckesanyMatch被优化以返回早,因此最小的时间花费在阅读了额外流(尤其是对于其中prio1是可能匹配的情况下)。实际上,anyMatch只会读取后备(prio2)情况下的整个数据流,因此对于平均运行,您只能遍历一个和一个分数列表长度。

  • 使用Collectors.groupingBy(...)方法在每种情况下构造一个Map和两个List,而上面的方法只能创建至多一个List。随着results的大小增加,这里的内存开销的差异将变得非常重要。对整个流进行分组,因此即使第一个元素碰巧通过prio1,每个元素都必须再次针对prio1.or(prio2)针对prio1进行检查。

  • groupingBy没有考虑prio1prio2不互相排斥的情况。如果prio2.test(e)可以返回true对于通过prio1的某些e,这些元素将在回退prio2列表中丢失。一次使用anyMatch和一个过滤器可以避免此问题。

  • 上述方法的行长度和复杂度对我来说似乎更容易管理。

+0

有没有办法将它写入单线程而没有多次迭代? – Ckkn

+0

我喜欢你的答案,但让我们尝试优化它运行一次 – Ckkn

+1

工作的想法..我在移动,所以挂:) :) – gyre

2

只是做一个条件:

final List<Foo> foo; 
if (results.stream().anyMatch(prio1)) { 
    foo = results.stream().filter(prio1).collect(Collectors.toList()); 
} else { 
    foo = results.stream().filter(prio2).collect(Collectors.toList()); 
} 

如果你真的想要一个衬垫,那么你可以做到以下几点,但有没有办法让周围流列表中的两倍。我会争辩说,if/else版本更清洁,更容易维护。

final List<Foo> foo = results.stream() 
    .filter(results.stream().anyMatch(prio1)? prio1 : prio2) 
    .collect(Collectors.toList()); 
+0

它应该是单线程......否则其他 – Ckkn

+3

可以通过将条件映射到属性来使其成为单线程,但是我不认为有一个班轮比上述有条件更好(否:anyMatch IS优化它会提前返回,这对prio1很有可能是很好的。 – eckes

+0

有没有办法将它写入到单线程没有多次迭代? – Ckkn

4

只是另一种方法是不使用anyMatch,但对结果操作之前,而组中的条目。

Optional.of(results.stream() 
        .filter(prio1.or(prio2)) 
        .collect(Collectors.groupingBy(prio1::test))) 
     .map(map -> map.getOrDefault(true, map.get(false))) 
     .ifPresent(System.out::println); 

我以前Optional,让你有一个“一个班轮”(刚刚格式化它,这样它会更易读)。您也可以使用orElseGet(Collections::emptyList)而不是ifPresent并将结果保存到List<String>

groupingBy把从prio1prio2过滤条目,将全部prio1 -matching项进入关键true,其余prio2 -matching项为false。如果我们在true中没有任何条目,那么将默认返回prio2已过滤的条目。如果没有任何prio1prio2匹配结果,则不会发生任何情况。

请注意,如果您直接返回Map,那么只有prio2匹配条目在false中匹配(如果您的筛选器是互斥的)。

+1

哦,这很酷!巧妙使用'groupingBy'。感谢教我新东西:) – gyre

+1

非常好,比我的黑客好得多。太糟糕了,你不能使用'partitioningBy()'。 'map()'函数虽然有点不必要的聪明。 '.map(map - > map.getOrDefault(true,map.get(false)))'在功能和性能上是等价的,只是稍微长一点,而且更易读,IMO。 – shmosel

+0

@shmosel你也可以使用'partitioningBy',但是你需要检查列表中是否有空,而现在我们只需检查'null'。你是对的......'getOrDefault'在这里更具可读性...会改变...... – Roland