使用java 8迭代和过滤两个列表
Posted
技术标签:
【中文标题】使用java 8迭代和过滤两个列表【英文标题】:iterating and filtering two lists using java 8 【发布时间】:2015-06-11 23:32:20 【问题描述】:我想迭代两个列表并获得新的过滤列表,其中的值不会出现在第二个列表中。有人可以帮忙吗?
我有两个列表 - 一个是字符串列表,另一个是 MyClass
对象列表。
List<String> list1;
List<MyClass> list2;
MyClass
MyClass(String val)
this.str = val;
String str;
...
...
我想要筛选的字符串列表基于 -> 检查第二个列表中的元素 (abc),其值不存在于 list1
中。
List<String> list1 = Arrays.asList("abc", "xyz", "lmn");
List<MyClass> list2 = new ArrayList<MyClass>();
MyClass obj = new MyClass("abc");
list2.add(obj);
obj = new MyClass("xyz");
list2.add(obj);
现在我想要新的过滤列表 -> 它将具有值 =>“lmn”。即 list2
中不存在的值,其元素在 list1
中。
【问题讨论】:
你想同时迭代这两个列表吗? 欢迎来到 ***!您提供一些代码很好,但要获得答案,您还可以尝试描述/展示您迄今为止尝试过的内容。 是的.. 我想同时迭代两个列表。首先是具有字符串列表的超集,其次是对象列表。第二个列表中的每个对象都有一个字符串字段,我想将其与第一个列表项进行比较。第二个列表中所有那些缺失的元素,我希望它们在一个列表中捕获。 【参考方案1】:// produce the filter set by streaming the items from list 2
// assume list2 has elements of type MyClass where getStr gets the
// string that might appear in list1
Set<String> unavailableItems = list2.stream()
.map(MyClass::getStr)
.collect(Collectors.toSet());
// stream the list and use the set to filter it
List<String> unavailable = list1.stream()
.filter(e -> unavailableItems.contains(e))
.collect(Collectors.toList());
【讨论】:
嗨 Ashley,什么 keyMapper 和 ValueMapper toMap() 需要传递? 我将 toMap 更改为 toSet。但它仍然没有按预期工作。不可用是没有得到任何元素。 抱歉 - 应该是 toSet,而不是 toMap - 在帖子中更正了它。也许您尝试实现的逻辑并不完全符合我的建议。您是在寻找列表 1 和列表 2 中的东西,还是试图找到列表 1 中而不是列表 2 中的东西?如果是第二个,那就放一个!在过滤器中 - 即.filter(e -> !unavailableItems.contains(e))
【参考方案2】:
这可以通过以下方式实现...
List<String> unavailable = list1.stream()
.filter(e -> !list2.contains(e))
.collect(Collectors.toList());
【讨论】:
随着列表 2 的大小增加到 3 以上,这种方式将非常低效。这是因为这种方式的逻辑类似于列表 1 中的每个元素扫描列表 2 的每个元素以找到匹配项如果扫描发现不匹配,则添加到结果中因此操作数类似于 list1 size * list 2 size 通过使用一组 list 2 和 contains 操作,您将进行大约 2x list1 size 的计算次数。跨度> 不要看“正确答案”复选标记。这与使用气泡的排序相同!它将有二次 O 时间。更好地使用来自***.com/a/29534646/907576的解决方案 这个答案对我有帮助/对我有用 - 我有两个 ArrayLists 对象。这些对象中有 5 个字段。我永远不会知道其中一个列表中的值是什么,所以我不能使用“包含” - 我必须为第一个列表中的每个项目遍历第二个列表,看看是否有 3 个字段相同.. . 大多数这类事情的例子都是非常做作的——只是一个字符串列表。综上所述,我认为我更喜欢嵌套的 for 循环,因为它更易于阅读和理解。【参考方案3】:用流做这件事既简单又易读:
Predicate<String> notIn2 = s -> list2.stream().noneMatch(mc -> s.equals(mc.str));
List<String> list3 = list1.stream().filter(notIn2).collect(Collectors.toList());
【讨论】:
有stream().noneMatch
简化一点【参考方案4】:
如果您流式传输第一个列表并使用基于包含在第二个列表中的过滤器...
list1.stream()
.filter(item -> !list2.contains(item))
下一个问题是您将在此流式处理操作的末尾添加什么代码以进一步处理结果...交给您。
另外,list.contains 很慢,所以使用集合会更好。
但是如果你使用集合,你可能会发现一些更简单的操作来处理这个问题,比如 removeAll
Set list1 = ...;
Set list2 = ...;
Set target = new Set();
target.addAll(list1);
target.removeAll(list2);
鉴于我们不知道您将如何使用它,因此实际上不可能建议采用哪种方法。
【讨论】:
你是绝对正确的。包含方法工作正常。但我的问题是,我的第二个列表不是简单的字符串列表。它的对象列表,我想将第一个列表与第二个列表中的对象属性之一进行比较。而且我不能做相反的方式,即将第一个列表转换为集合。因为第一个列表是一种超集。在比较两个列表之后,我希望新列表中缺少第二个列表中的元素,而这些元素存在于第一个列表中。【参考方案5】:list1 = list1.stream().filter(str1->
list2.stream().map(x->x.getStr()).collect(Collectors.toSet())
.contains(str1)).collect(Collectors.toList());
这可能会更有效。
【讨论】:
【参考方案6】:见下文,欢迎任何人对以下代码提供反馈。
两个数组之间不通用:
List<String> l3 =list1.stream().filter(x -> !list2.contains(x)).collect(Collectors.toList());
两个数组的共同点:
List<String> l3 =list1.stream().filter(x -> list2.contains(x)).collect(Collectors.toList());
【讨论】:
【参考方案7】:如果你有带 id 的类并且你想按 id 过滤
line1 : 你映射所有的 id
line2:过滤地图中不存在的内容
Set<String> mapId = entityResponse.getEntities().stream().map(Entity::getId).collect(Collectors.toSet());
List<String> entityNotExist = entityValues.stream().filter(n -> !mapId.contains(n.getId())).map(DTOEntity::getId).collect(Collectors.toList());
【讨论】:
虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。【参考方案8】:@DSchmdit 答案对我有用。我想补充一点。所以我的要求是根据表中存储的一些配置过滤文件。 该文件首先被检索并收集为 dto 列表。我从数据库接收配置并将其存储为另一个列表。这就是我使用流进行过滤的方式
List<FPRSDeferralModel> modelList = Files
.lines(Paths.get("src/main/resources/rootFiles/XXXXX.txt")).parallel().parallel()
.map(line ->
FileModel fileModel= new FileModel();
line = line.trim();
if (line != null && !line.isEmpty())
System.out.println("line" + line);
fileModel.setPlanId(Long.parseLong(line.substring(0, 5)));
fileModel.setDivisionList(line.substring(15, 30));
fileModel.setRegionList(line.substring(31, 50));
Map<String, String> newMap = new HashedMap<>();
newMap.put("other", line.substring(51, 80));
fileModel.setOtherDetailsMap(newMap);
return fileModel;
).collect(Collectors.toList());
for (FileModel model : modelList)
System.out.println("model:" + model);
DbConfigModelList respList = populate();
System.out.println("after populate");
List<DbConfig> respModelList = respList.getFeedbackResponseList();
Predicate<FileModel> somePre = s -> respModelList.stream().anyMatch(respitem ->
System.out.println("sinde respitem:"+respitem.getPrimaryConfig().getPlanId());
System.out.println("s.getPlanid()"+s.getPlanId());
System.out.println("s.getPlanId() == respitem.getPrimaryConfig().getPlanId():"+
(s.getPlanId().compareTo(respitem.getPrimaryConfig().getPlanId())));
return s.getPlanId().compareTo(respitem.getPrimaryConfig().getPlanId()) == 0
&& (s.getSsnId() != null);
);
final List<FileModel> finalList = modelList.stream().parallel().filter(somePre).collect(Collectors.toList());
finalList.stream().forEach(item ->
System.out.println("filtered item is:"+item);
);
细节在过滤谓词的实现中。与迭代循环和过滤掉相比,这证明了更好的性能
【讨论】:
【参考方案9】:`List<String> unavailable = list1.stream()
.filter(e -> (list2.stream()
.filter(d -> d.getStr().equals(e))
.count())<1)
.collect(Collectors.toList());`
for this if i change to
`List<String> unavailable = list1.stream()
.filter(e -> (list2.stream()
.filter(d -> d.getStr().equals(e))
.count())>0)
.collect(Collectors.toList());`
will it give me list1 matched with list2 right?
【讨论】:
以上是关于使用java 8迭代和过滤两个列表的主要内容,如果未能解决你的问题,请参考以下文章