【问题标题】:Update a list from another list从另一个列表更新列表
【发布时间】:2010-03-12 20:46:21
【问题描述】:

我在本地存储中有一个用户列表,我需要每隔一段时间从远程用户列表中更新它。基本上:

  1. 如果本地已存在远程用户,请更新其字段。
  2. 如果本地不存在远程用户,请添加该用户。
  3. 如果本地用户未出现在远程列表中,请停用或删除。
  4. 如果本地用户也出现在远程列表中,请更新其字段。 (同1)

例如。 远程列表:User(1, true), User(2, true), User(4, true), User(5, true)

本地列表:User(1, true), User(2, false), User(3, true), User(6, true)

新建本地列表:User(1, true), User(2, true), User(3, false), User(4, true), User(5, true), User(6, false),

只是同步本地列表的一个简单案例。在纯 Java 中是否有比以下更好的方法来做到这一点?看着自己的代码,我觉得很恶心。

public class User {
    Integer id;
    String email;
    boolean active;

    //Getters and Setters.......

    public User(Integer id, String email, boolean active) {
        this.id = id;
        this.email = email;
        this.active = active;
    }

    @Override 
    public boolean equals(Object other) {
        boolean result = false;
        if (other instanceof User) {
            User that = (User) other;
            result = (this.getId() == that.getId());
        }
        return result;
    }

}




public static void main(String[] args) {

    //From 3rd party
    List<User> remoteUsers = getRemoteUsers();

    //From Local store
    List<User> localUsers =getLocalUsers();     

    for (User remoteUser : remoteUsers) {
        boolean found = false;
        for (User localUser : localUsers) {
            if (remoteUser.equals(localUser)) {
                found = true;
                localUser.setActive(remoteUser.isActive());
                localUser.setEmail(remoteUser.getEmail());
                //update
            } 
            break;
        }
        if (!found) {
            User user = new User(remoteUser.getId(), remoteUser.getEmail(), remoteUser.isActive());
            //Save
        }
    }

    for(User localUser : localUsers ) {
        boolean found = false;
        for(User remoteUser : remoteUsers) {
            if(localUser.equals(remoteUser)) {
                found = true;
                localUser.setActive(remoteUser.isActive());
                localUser.setEmail(remoteUser.getEmail());
                //Update
            }
            break;
        }
        if(!found) {
            localUser.setActive(false);
            // Deactivate
        }
    }
}

【问题讨论】:

  • 1和4只需要做一次,它们是一样的
  • 您可以提取一些方法(例如User 类中的update(User user) 会将user 的字段设置为this)。您也可以在 for-comprehensions 中使用 java.util.Collections.binarySearch(list, user)

标签: java collections list-comprehension


【解决方案1】:

最好的方法是切换到不同的数据结构。 Map&lt;Integer, User&gt; 是最好的,因为用户可能有唯一的标识 ID。您选择的Map 实现可以是HashMap(预期O(1) 用于基本操作)或TreeMapO(log N))。

重要提示:你@Override equals(Object) 没有@Override hashCode()!!!这是危险的!您应该始终养成不覆盖或覆盖两者的习惯! (见: Overriding equals and hashCode in Java

所以,假设您有 Map&lt;Integer, User&gt; remoteUsersMap&lt;Integer, User&gt; localUsers

1.) 如果远程用户已在本地存在,请更新其字段。
4.) 如果本地用户也出现在远程列表中,请更新其字段。(同 1 )
2.) 如果本地不存在远程用户,请添加该用户。

查找remoteUsers 中的User 是否在localUsers 中可以通过简单的containsKeygetO(1)O(log N) 中回答。

for (int id : remoteUsers.keys()) {
   User local;
   if (localUsers.containsKey(id)) {
      local = localUsers.get(id);
   else {
      localUsers.put(id, local = new User(id));
   }
   local.updateFrom(remoteUsers.get(id));
}

3.) 如果本地用户未出现在远程列表中,请停用或删除。

以下解决方案展示了这些更高级的数据结构的强大功能:

Set<Integer> toDeactivate = new TreeSet<Integer>();
toDeactivate.addAll(localUsers.keySet());
toDeactivate.removeAll(remoteUsers.keySet());

for (int id : toDeactivate) {
   User local = localUsers.get(id);
   local.deactivate();
   localUsers.remove(id);
}

另外,如果你被List&lt;User&gt;卡住了,你仍然可以使用Map&lt;Integer, User&gt;作为这个处理的中间数据结构(基本上将List&lt;User&gt;转换为Map&lt;Integer, User&gt;,然后再转换回List&lt;User&gt;)。它仍然会更快,因为它是 O(N log N)O(N),与您现在拥有的 O(N^2) 相比。

如果您坚持只使用列表,那么您可能希望将其设为Collections.sort-ed 列表,这样您就可以对其进行Collections.binarySearch。您需要提供Comparator&lt;User&gt;,或制作User implements Comparable&lt;User&gt;,自然按id 排序。这也是O(N log N)

【讨论】:

  • 并非总是可以在ListSet 之间切换。这取决于域模型。
  • 您仍然可以使用Set 作为中介。它仍然更快,因为它是 O(N)O(N log N)O(N^2) 相反。
  • 见上一行:local = localUsers.get(remote); list.get 只需要一个整数索引而不是 Obj。
  • @Langali,该代码适用于 localUsersremoteUsersSet&lt;User&gt;,而不是 List&lt;User&gt;。阅读完整答案:如果可能,我建议转换数据结构,或使用中介。
  • 好的,我意识到Set 没有get,所以我改用Map,反正这样更有意义。
【解决方案2】:

您可以使用List.indexOf() 而不是遍历列表:

for (User remoteUser : remoteUsers) {
    int index = localUsers.indexOf(remoteUser);
    if (index >= 0) {
        User localUser = localUsers.get(index);
        localUser.setActive(remoteUser.isActive());
        localUser.setEmail(remoteUser.getEmail());
        //update
    } else {
        User user = new User(remoteUser.getId(), remoteUser.getEmail(), remoteUser.isActive());
        //Save
    }
}

【讨论】:

  • 但是如果没有列表理解或迭代,我如何获取更新参数?
  • 变量 localUser 未定义?
【解决方案3】:

兰加利语:

假设 Id 唯一标识一个用户,我有几个建议给你:

  • 创建一个类 User.Key(您的 User 类的内部类),并将 id 字段移到那里。让它成为最终的。仅使用 id 覆盖 User.Key 类的 hashcode 和 equals 方法:
公共用户{ 私钥最终密钥; ... 其他变量 公共静态类键{ 私有最终 int id; 公钥(最终 int id){ } // hashcode (可以是id) //等于(正如你已经实现的那样) } }
  • 创建一个地图来容纳您的用户。
    地图
    ;
  • 使用此地图来保存您的用户,然后使用getcontainsKey 方法找到您要查找的内容。

List.contains 的问题在于,在 ArrayList 上,它会对列表内容进行全面扫描。如果您对第二个列表的每个项目都这样做,那么您的性能是 O (n^2),这意味着当您将项目加倍时,您将乘以四运行您的方法所需的时间。 HashMap 的性能为 O (log(n)),这意味着如果您有 1000 个对象,运行它所需的时间仅慢 10 倍(大约)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2018-05-04
    • 2011-04-18
    相关资源
    最近更新 更多