不同对象类型列表之间的集合操作

Set operations between lists of different object types

我有两个列表,一个 List<User> localUsers 和一个 List<RemoteUser> remoteUsers。一个列表是服务器上的本地用户,另一个列表包含通过 gRPC 获取的来自另一台服务器的用户。它们可能是新的,存在于我们的系统中并需要更新,或者处于非活动状态(它们存在于第一个列表中但不存在于第二个列表中)。

为了确定哪些用户是哪些用户,我想在这些列表之间进行集合操作,但是列表是在不同类型的对象之间。为了解决这个问题,我找到了一个使用流来实现集合操作的方法,但是它们有些麻烦且难以阅读:

// This operation returns a difference between remote users and local users
// signifies new users that don't yet exist on the server
List<RemoteUser> newUsers = remoteUsers.stream()
.filter(remoteUser ->
localUsers.stream().noneMatch(
localUser ->
localUser.getUserName().equals(checkAddPrefix(remoteUser.getLogin())))).collect(Collectors.toList());

我意识到我可以通过某种双重 for 循环来消除流的歧义,但无论如何...

查找非活跃用户与上面相同,只是交换了列表。

两个组之间的共同用户可以用

执行
remoteUsers.removeAll(newRemoteUsers)

是否有更简洁的方法来在不同对象类型的列表之间执行集合操作?差分运算的时间复杂度为O(m * n) for m = len(localusers), n = len(remoteUsers).

我能比这更好吗?

正如 Jim Garrison 所建议的那样,您可以创建一个 class 来根据用户定义 equals()hashCode()登录等凭据同时将存储对实例 UserRemoteUser 的引用。这样就可以比较这些 wrapper objects,然后提取 UserRemoteUser.

的原始实例

客户端代码可能如下所示:

public static void main(String[] args) {
    List<UserData> remoteData = remoteUsers.stream()
            .map(remUser -> new UserData(remUser.getLogin(), remUser, null))
            .collect(Collectors.toList());

    Set<UserData> localData = localUsers.stream()
            .map(locUser -> new UserData(locUser.getUserName(), null, locUser))
            .collect(Collectors.toSet());

    remoteData.removeAll(localData); // will perform in O(n) time

    List<RemoteUser> newRemoteUsers = remoteData.stream()
            .map(UserData::getRemoteUser)
            .collect(Collectors.toList());
}

请注意,在这种情况下,removeAll() 方法的时间复杂度将为 O(n),而不是 O(n * m) 。如果您查看源代码,您会发现此操作是通过列表的单次传递执行的,并且对于列表方法 contains() 中的每个元素,将在作为参数传递的集合上调用。因为在这种情况下,它是 Set contains() 的成本是 O(1) 总时间复杂度是 O(n).

包装器 class 可能如下:

public class UserData{
    private final String login;
    private final RemoteUser remoteUser;
    private final User localUser;

    public UserData(String login, RemoteUser remoteUser, User localUser) {
        this.login = login;
        this.remoteUser = remoteUser;
        this.localUser = localUser;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof UserData other) {
            return this.login.equals(other.login);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return Objects.hash(login);
    }

    // getters
}