Java 8 Lambda通过列表进行过滤

51

我有两个列表,我想过滤掉这些元素,这些元素同时存在于两个列表中。我想使用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);
        }
    }
}

3
你的代码无法编译;User不是Client,除非User继承了Client - fge
4个回答

100
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:

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)吗? - Jeremy G
4
在List上调用contains()方法会遍历整个列表。但在HashSet上调用contains()方法只会计算hashCode并遍历与该hashCode相应的桶,通常该桶包含0或1个元素。HashSet的contains查找时间复杂度为O(1)。 - JB Nizet
2
啊,我明白了。在列表上调用.contains()的时间复杂度是O(n),但在HashSet上是O(1)。感谢您的澄清! - Jeremy G

8

看这里:

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() 对用户进行去重,则可以提高效率。 - Lam Le

6

我想分享一个例子,以了解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()将获取集合的流,该流将返回Integer的Stream。
filter:返回与给定条件匹配的流。即根据给定条件(i -> i%2!= 0)返回匹配的流。
collect:无论基于过滤器条件筛选的Integer流是什么,这些整数都将放入列表中。

2

Something like:

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

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


1
我们这里不是通过对象的“equals”方法来进行比较。请重新阅读问题。 - Pshemo
1
我看到你在回答中纠正了方法,但仍存在一些小问题。如果你没有先测试过,请不要发布答案。删除它以避免进一步的投票和混淆,并在暗处进行更正,在正确时再将其恢复。 - Pshemo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接