【问题标题】:Java 8: Merging two Lists containing objects by keyJava 8:按键合并两个包含对象的列表
【发布时间】:2017-01-12 12:02:19
【问题描述】:

我有两个列表:

List<Server> servers1 = new ArrayList<>();
Server s1 = new Server("MyServer");
s1.setAttribute1("Attribute1");
servers1.add(s1);

List<Server> servers2 = new ArrayList<>();
Server s2 = new Server("MyServer");
s2.setAttribute2("Attribute2");
servers2.add(s2);

servers1 包含具有nameattribute1 的服务器(但没有attribute2)。
servers2 包含具有nameattribute2 的服务器(但没有attribute1 )。

public class Server {
   private String name;
   private String attribute1;
   private String attribute2;

   public Server(String name) { 
       this.name = name;
       this.attribute1 = "";
       this.attribute2 = "";
   }

   //Getters & Setters

}

有谁知道如何将这两个列表合并为一个包含 每个Server 仅一次(name)但同时具有两个属性?

有些服务器只存在于一个或另一个列表中。 最终列表应包含所有服务器。

    List<Server> servers1 = new ArrayList<>();
    Server s1 = new Server("MyServer");
    s1.setAttribute1("Attribute1");

    Server s2 = new Server("MyServer2");
    s2.setAttribute1("Attribute1.2");

    servers1.add(s1);
    servers1.add(s2);

    List<Server> servers2 = new ArrayList<>();
    Server s3 = new Server("MyServer");
    s3.setAttribute2("Attribute2");

    Server s4 = new Server("MyServer3");
    s4.setAttribute2("Attribute2.2");

    servers2.add(s3);
    servers2.add(s4);

应该导致:

[服务器[name=MyServer,attribute1=Attribute1,attribute2=Attribute2],

服务器 [name=MyServer2,attribute1=Attribute1.2,attribute2=]]

服务器 [name=MyServer3, attribute1=, attribute2=Attribute2.2]]

//解决方案(感谢大家的帮助!)

Map<String, Server> serverMap1 = Stream.concat(servers1.stream(), servers2.stream())
            .collect(Collectors.toMap(Server::getName, Function.identity(), 
            (server1, server2) -> {
                server1.setAttribute2(server2.getAttribute2()); 
                return server1; 
            }));

【问题讨论】:

  • servers1 创建一个nameServer 的映射,然后从servers2 填充缺少的attribute2,按名称在映射中查找服务器。尝试自己编写一些代码。
  • 如果这是您的Server 类的全部内容,那么List 中的实体如何具有nameattribute1attribute2 的任何值。如果这不是整个类,请包括构造函数
  • 这两个列表是否始终包含相同的服务器或存在不会合并的服务器?
  • 好问题。有些服务器不会被合并。最后我需要所有服务器,如果它们可以合并,应该。
  • 我尝试了同样的事情。我没有获取第一个列表中不存在的第二个列表对象的数据。在这种情况下,对于 server3

标签: java list java-8 java-stream


【解决方案1】:

将每个列表转换为映射并合并它(我使用 Lombok 不编写样板代码):

@Data
@NoArgsConstructor
@AllArgsConstructor
class Server {
    private String name;
    private String attribute1;
    private String attribute2;
}

public class ServerMain {

    public static void main(String[] args) {

        List<Server> servers1 = Arrays.asList(
                new Server("name1", "attr1.1", null),
                new Server("name2", "attr1.2", null));

        List<Server> servers2 = Arrays.asList(
                new Server("name1", null, "attr2.1"),
                new Server("name2", null, "attr2.2"));

        Map<String, Server> serverMap1 = servers1.stream().collect(Collectors.toMap(Server::getName, Function.identity()));
        Map<String, Server> serverMap2 = servers2.stream().collect(Collectors.toMap(Server::getName, Function.identity()));

        serverMap1.keySet().forEach(key -> serverMap1.merge(key,
                serverMap2.get(key),
                (server1, server2) -> {
                    server1.setAttribute2(server2.getAttribute2());
                    return server1;
                }));


        System.out.println(serverMap1);
    }
}

【讨论】:

  • 你做的工作太多,收集到两个地图中为每个服务器做两次地图查找。您可以完全删除serverMap2,然后执行servers2.forEach(s -&gt; serverMap1.merge(s.getName(), s, /* the same merge function as before */));
  • 如果您确信密钥是唯一的,您甚至可以在单个 Stream 操作中做到这一点:serverMap1 = Stream.concat(servers1.stream(), servers2.stream()) .collect(Collectors.toMap( Server::getName, Function.identity(), /* the same merge function as before */));
  • @Holger 我建议将您的评论转换为答案,它看起来只是基于“优雅”流的答案。
  • servers1 中不存在的服务器也应该在最终列表中(即使它只包含一个属性)
  • @Robin,见 Holger 的第二条评论
【解决方案2】:

对于servers1 中的每个服务器,在servers2 中找到一个匹配的server2,并将第一个缺失的属性设置为正确的属性,否则如果没有名称匹配的服务器,则返回null。另请注意,此实现将仅返回 server1 中包含的服务器名称列表。

@AllArgsConstructor
@Data
private class Server {
   private String name;
   private String attribute1;
   private String attribute2;
}

List<Server> completServersInfo = servers1.stream()
     .map((server1 -> {
        Server matchingServer = servers2.stream()
           .filter(server2 -> server2.name.equals(server1.name))
           .findFirst()
           .orElse(null);
        server1.setAttribute2(matchingServer.attribute2);
        return server1;
     }))
     .collect(Collectors.toList());

【讨论】:

  • 如果您确信两个列表中存在相同的键 Map serverMap1 = servers1.stream().collect(Collectors.toMap(Server::getName, Function.identity()) );服务器2 .forEach(key->serverMap1.get(key.getName()).setAttribute2(key.getAttribute2())); System.out.println(serverMap1);
  • 两个列表中不需要存在相同的键。如果没有,服务器应该按原样出现在最终列表中(缺少参数)
【解决方案3】:

也许您可以使用地图来做到这一点?映射的键是服务器的名称,映射的值是对象Server

Map<String, Server> map = new HashMap<>();
for (Server s : servers1) {
    map.put(s.getName(), s);
}
for (Server s : servers2) {
    String key = s.getName();
    if (map.containsKey(key)) {
        map.get(key).setAttribute2(s.getAttribute2());
    } else {
        map.put(key, s);
    }
}
List<Server> servers = new ArrayList<>(map.values()); // or other impl.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多