Ste*_*ier 17 java java-8 java-stream
假设我已经List<Car>并且我想搜索该列表以验证我同时拥有思域和焦点.如果它是一个OR,它很容易,因为我可以在它上面应用OR .filter().请记住,我不能filter().filter()为这种类型的AND做.
一个有效的解决方案是:
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;
Run Code Online (Sandbox Code Playgroud)
但后来我基本上处理了两次列表.我不能&&在过滤器中应用,也不能filter().filter().
有没有办法处理流一次,以查找列表是否包含思域和焦点汽车?
重要更新:提供的解决方案的关键问题是它们都保证O(n),而我的解决方案可以在两次比较后完成.如果我的汽车列表是1000万辆汽车,那么将会有非常显着的性能成本.然而,我的解决方案感觉不对,但也许这是性能最佳的解决方案......
AJN*_*eld 18
您可以过滤流"Civic" or "Focus",然后在getModel()返回a时运行收集器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");
Run Code Online (Sandbox Code Playgroud)
但是,这将处理整个流; 当两者都被发现时,它不会"快速成功".
以下是"快速成功"的短路方法.(更新以包括评论和评论的澄清,如下)
return reportElements.stream()
.map(Car::getModel)
.filter(model -> model.equals("Focus") || model.equals("Civic"))
.distinct()
.limit(2)
.count() == 2;
Run Code Online (Sandbox Code Playgroud)
我们一次打破一个流操作,我们有:
.map(Car::getModel)
Run Code Online (Sandbox Code Playgroud)
该操作将汽车流转换为汽车模型流.我们这样做是为了提高效率 我们不是car.getModel()在管道的其余部分中的多个位置多次调用(两次在filter(...)针对每个所需模型进行测试,再次用于distinct()操作),而是应用此映射操作一次.请注意,这不会创建评论中提到的"临时地图"; 它只是将汽车转换为汽车下一阶段管道的模型.
.filter(model -> model.equals("Focus") || model.equals("Civic"))
Run Code Online (Sandbox Code Playgroud)
这会过滤汽车模型流,只允许"焦点"和"思域"汽车模型通过.
.distinct()
Run Code Online (Sandbox Code Playgroud)
此管道操作是有状态的中间操作.它会记住它在临时看到的每个汽车模型Set.(这可能是评论中提到的"临时地图".)只有在临时集中不存在模型时,它才会被(a)添加到集合中,并且(b)传递到下一阶段管道.
在流水线的这一点上,流中最多只能有两个元素:"Focus"或"Civic",或者两者都没有.我们知道这一点,因为我们知道filter(...)只会通过这两个模型,我们知道这distinct()将删除任何重复.
但是,这个流管道本身并不知道.它将继续将汽车对象传递到map舞台以转换为模型字符串,将这些模型传递到filter舞台,并将任何匹配的项目发送到distinct舞台.它不能说这是徒劳的,因为它不明白没有别的东西可以通过算法; 它很简单地执行指令.
但我们明白了.最多两个不同的模型可以通过该distinct()阶段.所以,我们遵循这个:
.limit(2)
Run Code Online (Sandbox Code Playgroud)
这是一种短路状态中间操作.它保持通过的项目数的计数,并且在指示的数量之后,它终止流,导致所有后续项目被丢弃,甚至没有开始管道.
在流水线的这一点上,流中最多只能有两个元素:"Focus"或"Civic",或者两者都没有.但如果两者都有,那么流已被截断并且结束了.
.count() == 2;
Run Code Online (Sandbox Code Playgroud)
计算通过管道的项目数量,并根据所需的数量进行测试.
如果我们找到两个模型,流将立即终止,count()将返回2,并将true返回.当然,如果两个模型都不存在,则处理流直到苦味结束,count()将返回小于2的值,并且false将导致.
例如,使用无限的模型流.每个第三个模型都是"思域",每个第7个模型都是"焦点",其余的都是"模型#":
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);
Run Code Online (Sandbox Code Playgroud)
输出:
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
Run Code Online (Sandbox Code Playgroud)
请注意,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();
Run Code Online (Sandbox Code Playgroud)
这里,给定任意models组,可以获得任何特定汽车模型组的存在,不仅限于2.
| 归档时间: |
|
| 查看次数: |
4351 次 |
| 最近记录: |