【问题标题】:Java 8 Lambda filter by ListsJava 8 Lambda 按列表过滤
【发布时间】:2015-07-12 18:51:52
【问题描述】:

我有两个列表,我想过滤列表中包含的那些元素。我想用 lambda 表达式来做到这一点。

Users getName 和 Clients getUserName 都返回字符串。

这是我的示例代码:

List<Client> clients = new ArrayList<>();
List<User> users = new ArrayList<>();
List<Client> results = new ArrayList<>();

for (Client user : users) {
    for(Client client: clients){
        if(user.getName().equals(client.getUserName())){
            result.add(client);
        }
    }
}

【问题讨论】:

  • 你的代码不会编译; User 不是 Client,除非 User 继承 Client

标签: java lambda java-8


【解决方案1】:
Predicate<Client> hasSameNameAsOneUser = 
    c -> users.stream().anyMatch(u -> u.getName().equals(c.getName()));

return clients.stream()
              .filter(hasSameNameAsOneUser)
              .collect(Collectors.toList());

但这是非常低效的,因为它是 O(m * n)。您最好创建一组可接受的名称:

Set<String> acceptableNames = 
    users.stream()
         .map(User::getName)
         .collect(Collectors.toSet());

return clients.stream()
              .filter(c -> acceptableNames.contains(c.getName()))
              .collect(Collectors.toList());

另请注意,它并不严格等同于您拥有的代码(如果已编译),如果多个用户与客户端具有相同的名称,则会将相同的客户端两次添加到列表中。

【讨论】:

  • 我很好奇第二个实现如何更有效?看起来它对我来说仍然是 O(m * n)。当我们执行acceptableNames.contains(...) 时,我们正在遍历该集合。是否认为 Set 会清除重复项,因此 O(m * n) 稍微好一些?
  • 没有。在 List 上调用 contains() 会遍历该列表。但是在 HashSet 上调用 contains 只会计算 hashCode 并遍历与该 hashCode 对应的存储桶,该存储桶通常包含 0 或 1 个元素。一个 HashSet 包含查找是 O(1)。
  • 啊,我明白了。在列表上调用 .contains() 将是 O(n),但在 HashSet 上调用 O(1)。感谢您的澄清!
【解决方案2】:

看这个:

List<Client> result = clients
    .stream()
    .filter(c -> 
        (users.stream().map(User::getName).collect(Collectors.toList())).contains(c.getName()))
        .collect(Collectors.toList());

【讨论】:

  • users.stream().map(User::getName).collect(Collectors.toList()) 使用 Collectors.toSet() 让用户区分 user.name 效率更高
【解决方案3】:

我想分享一个例子来了解stream().filter的用法

代码片段:识别偶数的示例程序。

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public void fetchEvenNumber(){
        List<Integer> numberList = new ArrayList<>();
        numberList.add(10);
        numberList.add(11);
        numberList.add(12);
        numberList.add(13);
        numberList.add(14);
        numberList.add(15);

        List<Integer> evenNumberListObj = numberList.stream().filter(i -> i%2 == 0).collect(Collectors.toList());
        System.out.println(evenNumberListObj);
}

输出将是:[10,12,14]

列出 evenNumberListObj = numberList.stream().filter(i -> i%2 == 0).collect(Collectors.toList());

numberList:它是一个包含数字列表的ArrayList对象。

java.util.Collection.stream() : stream() 会得到collection的流,它会返回Integer的Stream。

filter:返回匹配给定谓词的流。即基于给定条件 (i -> i%2 != 0) 返回匹配流。

收集:无论基于过滤条件的整数过滤器的流,这些整数都将被放入一个列表中。

【讨论】:

    【解决方案4】:

    类似:

    clients.stream.filter(c->{
       users.stream.filter(u->u.getName().equals(c.getName()).count()>0
    }).collect(Collectors.toList());
    

    然而,这并不是一种非常有效的方法。除非集合非常小,否则您最好构建一组用户名并在条件下使用它。

    【讨论】:

    • 我们在这里不是通过equals 方法比较对象。请重新阅读问题。
    • 我看到您在回答中纠正了方法,但仍然存在一些小问题。如果您没有先测试,请不要发布答案。删除它以避免进一步的投票和混乱,在阴影中更正它并在正确时取消删除它并没有什么可耻的。
    猜你喜欢
    • 2018-06-07
    • 1970-01-01
    • 2017-06-21
    • 2019-01-31
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多