不同对象类型列表之间的集合操作
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()
登录等凭据同时将存储对实例 User
和 RemoteUser
的引用。这样就可以比较这些 wrapper objects,然后提取 User
或 RemoteUser
.
的原始实例
客户端代码可能如下所示:
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
}
我有两个列表,一个 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()
登录等凭据同时将存储对实例 User
和 RemoteUser
的引用。这样就可以比较这些 wrapper objects,然后提取 User
或 RemoteUser
.
客户端代码可能如下所示:
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
}