Nibor
Nibor

Reputation: 679

Java 8: Merging two Lists containing objects by key

I have two lists:

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 contains servers with a name and an attribute1 (but no attribute2).
servers2 contains servers with a name and an attribute2 (but no 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

}

Does anyone know how I can merge these two lists to one list containing each Server only once (by name) but with both attributes?

There are servers which only exist in one or the other list. The final list should contain all servers.

    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);

should result in:

[Server [name=MyServer, attribute1=Attribute1, attribute2=Attribute2],

Server [name=MyServer2, attribute1=Attribute1.2, attribute2=]]

Server [name=MyServer3, attribute1=, attribute2=Attribute2.2]]

//SOLUTION (thx everybody for the help!)

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; 
            }));

Upvotes: 16

Views: 26332

Answers (3)

Mincong Huang
Mincong Huang

Reputation: 5552

Maybe you can use a map to do it? The key of the map would be the name of the server and the value of the map is the object 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.

Upvotes: 2

Ivan Falanga
Ivan Falanga

Reputation: 21

For each server in servers1 find a matching server2 in servers2 and set the missing attribute of the first one to correct attribute else if there is no matching server by name simply return null. Also note that this implementation will return only the list of servers name contained in servers1.

@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());

Upvotes: 2

Dmitry Gorkovets
Dmitry Gorkovets

Reputation: 2286

Convert each list to map and merge it (I use Lombok to not write boilerplate code):

@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);
    }
}

Upvotes: 4

Related Questions