我们说我List<Car>
并且我想搜索该列表以验证我同时拥有思域和焦点。如果它是OR,那么我很容易在.filter()
上应用OR。请注意,对于此类AND,我无法filter().filter()
。
一个可行的解决方案是:
boolean hasCivic = reportElements.stream()
.filter(car -> "Civic".equals(car.getModel()))
.findFirst()
.isPresent();
boolean hasFocus = reportElements.stream()
.filter(car -> "Focus".equals(car.getModel()))
.findFirst()
.isPresent();
return hasCivic && hasFocus;
但是我基本上处理了两次列表。我无法在过滤器中应用&&
,也无法filter().filter()
。
有没有办法处理流一次,以查找列表是否同时包含Civic和Focus车?
重要更新:提供的解决方案的关键问题是它们都保证O(n),而我的解决方案可以在两次比较后完成。如果我的汽车列表是1000万辆汽车,那么将会有非常显着的性能成本。然而,我的解决方案感觉不对,但也许这是性能最佳的解决方案......
答案 0 :(得分:13)
您可以在"Civic" or "Focus"
上过滤流,然后在getModel()
上运行一个收藏家,返回Set<String>
。然后你可以测试你的集合是否包含两个键。
Set<String> models = reportElements.stream()
.map(Car::getModel)
.filter(model -> model.equals("Focus") || model.equals("Civic"))
.collect(Collectors.toSet());
return models.contains("Focus") && models.contains("Civic");
然而,这将处理整个流;它不会快速成功&#34;当两者都被发现时。
以下是&#34;快速成功&#34;短路方法。 (更新以包括评论和评论的说明,如下)
return reportElements.stream()
.map(Car::getModel)
.filter(model -> model.equals("Focus") || model.equals("Civic"))
.distinct()
.limit(2)
.count() == 2;
一次打破一个流操作,我们有:
.map(Car::getModel)
此操作将汽车流转换为汽车模型流。
我们这样做是为了提高效率
而不是在管道的其余部分中的多个位置多次调用car.getModel()
(在filter(...)
中两次以针对每个所需模型进行测试,并再次针对distinct()
操作),我们应用此映射操作一次。
请注意,这不会创建&#34;临时地图&#34;在评论中提到;
它只是将汽车转换为汽车下一阶段管道的车型。
.filter(model -> model.equals("Focus") || model.equals("Civic"))
这会过滤汽车模型流,只允许&#34; Focus&#34;和&#34; Civic&#34;汽车模型通过。
.distinct()
此管道操作是stateful intermediate operation。
它会记住它在临时Set
中看到的每个汽车模型。
(这可能是评论中提到的&#34;临时地图&#34;)
仅当临时集中不存在模型时,
它是否会(a)被添加到集合中,并且(b)被传递到管道的下一阶段。
在管道中的这一点,流中最多只能有两个元素:&#34; Focus&#34;或&#34; Civic&#34;或者两者都没有。
我们知道这一点,因为我们知道filter(...)
只会传递这两个模型,我们知道distinct()
会删除所有重复项。
但是,这个流管道本身并不知道。
它会继续将汽车对象传递到map
阶段以转换为模型字符串,将这些模型传递到filter
阶段,并将任何匹配的项目发送到distinct
阶段。
它无法分辨这是徒劳的,因为它不能理解没有其他任何东西可以通过算法;它很简单地执行说明。
但 我们 明白了。
最多两个不同的模型可以通过distinct()
阶段。
所以,我们遵循这个:
.limit(2)
这是 short-circuiting stateful intermediate operation。 它保持通过的项目数量,和 在指示的数量之后,它终止流,导致所有后续项目被丢弃,甚至没有开始管道。
在管道中的这一点,流中最多只能有两个元素:&#34; Focus&#34;或&#34; Civic&#34;或者两者都没有。 但如果两者都有,那么流已经被截断并且最后了。
.count() == 2;
计算通过管道的物品数量, 并根据所需的数字进行测试。
如果我们找到两个模型,流将立即终止,count()
将返回2,并且将返回true
。
如果两个模型都不存在,当然会处理流,直到痛苦结束,count()
将返回小于2的值,并且false
将会产生。
示例,使用无限的模型流。 每三个模型都是一个&#34; Civic&#34;,每个第7个模型都是&#34; Focus&#34;其余的都是&#34;模型#&#34;:
boolean matched = IntStream.iterate(1, i -> i + 1)
.mapToObj(i -> i % 3 == 0 ? "Civic" : i % 7 == 0 ? "Focus" : "Model "+i)
.peek(System.out::println)
.filter(model -> model.equals("Civic") || model.equals("Focus"))
.peek(model -> System.out.println(" After filter: " + model))
.distinct()
.peek(model -> System.out.println(" After distinct: " + model))
.limit(2)
.peek(model -> System.out.println(" After limit: " + model))
.count() == 2;
System.out.println("Matched = "+matched);
输出:
Model 1
Model 2
Civic
After filter: Civic
After distinct: Civic
After limit: Civic
Model 4
Model 5
Civic
After filter: Civic
Focus
After filter: Focus
After distinct: Focus
After limit: Focus
Matched = true
请注意,3个模型已通过filter()
,但只有2个模型超过了distinct()
和limit()
。
更重要的是,请注意在达到无限模型流结束之前很久就返回了true
。
推广解决方案,因为OP需要可以与人,信用卡或IP地址等一起使用的东西,搜索条件可能不是两个固定的项目集:
Set<String> models = Set.of("Focus", "Civic");
return reportElements.stream()
.map( Car::getModel )
.filter( models::contains )
.distinct()
.limit( models.size() )
.count() == models.size();
在这里,给定任意models
组,可以获得任何特定车型的存在,而不仅限于2。
答案 1 :(得分:3)
你可以这样做:
reportElements.stream()
.filter(car -> "Civic".equals(car.getModel()) || "Focus".equals(car.getModel()))
.collect(Collectors.toMap(
c -> c.getModel(),
c -> c,
(c1, c2) -> c1
)).size() == 2;
甚至是Set
reportElements.stream()
.filter(car -> "Civic".equals(car.getModel()) || "Focus".equals(car.getModel()))
.map(car -> car.getModel())
.collect(Collectors.toSet())
.size() == 2;
和distinct
reportElements.stream()
.filter(car -> "Civic".equals(car.getModel()) || "Focus".equals(car.getModel()))
.map(car -> car.getModel())
.distinct()
.count() == 2L;
答案 2 :(得分:1)
它“感觉不对”的原因是因为您强制流API执行它不想做的事情。使用传统循环几乎肯定会更好:
-fsigned-char