【问题标题】:Merge three HashMap<String, POJO> with unique key用唯一键合并三个 HashMap<String, POJO>
【发布时间】:2019-01-13 00:05:32
【问题描述】:

我有三个这种泛型类型的 HashMap HashMap&lt;String,Server&gt; 但我想根据我的唯一键组合所有 HashMap 的数据。但是只有服务器 pojo 有不同的数据。

所以第一个 hashmap 有一些服务器信息,如 ip_addressserver name 因此我在 map 和 ip_address 作为键 中都有这两个值。然后我在其他地图中存储了一些其他硬件规范,并将 ip_address 作为键,在第三张地图中也是如此。

因此,根据key组合所有POJO我会得到完整的服务器信息和对应的ip_address。

不做嵌套操作不知道怎么办

【问题讨论】:

  • @Logan logan 如果我将 map1 值放入 map2 并且它们具有相同的密钥,它将仅用硬件规格替换 map2 新服务器。服务器是 POJO
  • 你能举几个例子吗?
  • 对每个 entrySet 进行合并流,然后使用 groupingBy 进行合并归约。

标签: java collections


【解决方案1】:

使用putAll():

Map<String, Server> all = new HashMap<>();
all.putAll(map1);
all.putAll(map2);
all.putAll(map3);

如果您想要单线:

Map<String, Server> all = Stream.of(map1, map2, map3)
    .reduce(new HashMap<>(), (a, b) -> {a.putAll(b); return a;});

碰撞导致替换。

【讨论】:

  • 根据我对答案的阅读,OP 希望合并每个地图中的值。
  • @boris 你如何合并服务器?我假设给定密钥遇到的最后一个服务器是赢家。
  • OP 谈到第一张地图有一些信息,而其他地图有其他信息。我想他们都是部分人口。至于如何特别合并服务器 - 这可能是 OP 的一个;如果它们只是豆子,那么我想一大堆if(a.getCCC!= null) b.setXXX() 类型的操作......
  • 如果您在Stream 上调用reduce 或使用缩减Collector 我相信该值必须是不可变的并且缩减关联 - 这与Stream 如何并行缩减有关。
【解决方案2】:

这是一种方法。我已经展示了仅使用两个 Map 集合的示例代码,第三个可以以类似的方式应用。

我正在使用MapcomputeIfPresent 方法来“合并”两个映射的值。 BiFunctioncomputeIfPresent 的参数)需要处理“合并”方面 - 任何验证等,目前不知道(对我来说)。另外,请参阅下面关于 computeIfPresent 的注释。

示例代码:

import java.util.*;
import java.util.function.*; 

public class CombiningMaps {

    private static Map<String, ServerInfo> map1 = new HashMap<>();
    private static Map<String, ServerInfo> map2 = new HashMap<>();

    private static BiFunction<String, ServerInfo, ServerInfo> combineWithMap2 = 
        (k, v) -> {
                    if (map2.get(k) != null) {
                        ServerInfo v2 = map2.get(k);
                        // combine values of map1 and map2
                        v.setHw2(v2.getHw2());
                    }
                    return v;
                };

    public static void main(String [] args) {

        // Add some test data to map 1
        map1.put("serv1", new ServerInfo("0A", "a1", "",""));
        map1.put("serv2", new ServerInfo("0B", "b1", "",""));
        map1.put("serv3", new ServerInfo("0C", "c1", "",""));
        System.out.println(map1);

        // Add some data to map 2
        map2.put("serv1", new ServerInfo("0A", "", "a2",""));
        map2.put("serv2", new ServerInfo("0B", "", "b2",""));
        map2.put("serv3", new ServerInfo("0C", "", "c2",""));
        System.out.println(map2);

        // Update map1 with map 2's info
        map1.forEach((k,v) -> map1.computeIfPresent(k, combineWithMap2));
        System.out.println(map1);
    }
}

class ServerInfo {
    private String ipAddr;
    private String hw1; // hw stands for hardware related info
    private String hw2;
    private String hw3;
    public ServerInfo(String ipAddr, String hw1, String hw2, String hw3) {
        this.ipAddr = ipAddr;
        this.hw1 = hw1;
        this.hw2 = hw2;
        this.hw3 = hw3;
    }
    public String getIpAddr() {
        return ipAddr;
    }
    public String getHw1() {
        return hw1;
    }
    public void setHw1(String s) {
        hw1 = s;
    }
    public String getHw2() {
        return hw2;
    }
    public void setHw2(String s) {
        hw2 = s;
    }
    public String getHw3() {
        return hw3;
    }
    public void setHw3(String s) {
        hw3 = s;
    }
    public String toString() {
        return ipAddr + ":" + hw1 + "-" + hw2 + "-" +  hw3;
    }
}


输出:

{serv2=0B:b1--, serv3=0C:c1--, serv1=0A:a1--}
{serv2=0B:-b2-, serv3=0C:-c2-, serv1=0A:-a2-}
{serv2=0B:b1-b2-, serv3=0C:c1-c2-, serv1=0A:a1-a2-}


computeIfPresent 的行为方式(某些场景):

考虑一个带有键和值的 Map&lt;String, Integer&gt; 映射:{four=4, one=1, ten=10, two=2, three=3, five=5, eleven=11, twelve=null}

(1) 使用新值更新映射(注意 lambda 是 BiFunction 返回一个新计算的值):

map.computeIfPresent("ten", (k, v) -> new Integer(100));

(2)函数返回一个null,现有的映射被移除:

map.computeIfPresent("eleven", (k, v) -> null);

(3) 不添加映射,因为不存在映射:

map.computeIfPresent("twenty", (k, v) -> new Integer(20));

(4)现有值为null,所以没有变化:

map.computeIfPresent("twelve", (k, v) -> new Integer(12));

【讨论】:

  • 结果没有产生,而且当我只运行它的 map1 值时,请看我做到了并发布答案
  • 我不关注你的评论。
  • 好吧,你的方法可行我犯了一个小错误,但是如果我想合并第三张地图会发生什么?由于在 Bifunction 中,如果您为第三张地图设置字段,它可能会覆盖我们可以拥有另一个 Bifunction
  • 添加第三张地图:map1.forEach((k,v) -&gt; map1.computeIfPresent(k, combineWithMap3));combineWithMap3 是一个新的BiFunction,使用map3
  • 感谢您的回答。只有您可以决定使用哪一个。从清晰度和可维护性的角度考虑一个。此外,人们可以即兴发挥这些解决方案。
【解决方案3】:

在获得三个地图中的所有值后,我使用 finalMap 来组合值。这是它的工作,因为所有地图中的密钥都是相同的,因此使用密钥获取地图的密钥 map1 是个好主意。

Set<Map.Entry<String, Server>> set1 = map.entrySet();  
                    for (Map.Entry<String, Server> me : set1) {
                        Server server=new Server();
                                     server.setIp_Address(me.getKey());
                                     server.setServerName(me.getValue().getServerName());
                                     server.setOsName(map1.get(me.getKey()).getOsName());
                                     server.setOsVersion(map1.get(me.getKey()).getOsVersion());
                                     server.setOsArchitecture(map1.get(me.getKey()).getOsArchitecture());
                                     server.setHardDiskCapacity(map1.get(me.getKey()).getHardDiskCapacity());
                                     server.setRamCapacity(map1.get(me.getKey()).getRamCapacity());
                                     server.setAvgNetWorkUtilizationSent(map2.get(me.getKey()).getAvgNetWorkUtilizationSent());
                                     server.setAvgNetworkUtilizationReceived(map2.get(me.getKey()).getAvgNetworkUtilizationReceived());
                                     server.setAvgCPUtilization(map2.get(me.getKey()).getAvgCPUtilization());
                                     server.setAvgRamUtilization(map2.get(me.getKey()).getAvgRamUtilization());
                                     finalMap.put(me.getKey(), server);
                   }
                    Set<Map.Entry<String, Server>> set2 = finalMap.entrySet();  
                    for (Map.Entry<String, Server> me : set2) {
            System.out.println(" ServerIP : "+ me.getValue().getIp_Address()+"\t"+" Server Name :"+me.getValue().getServerName()+"\t \t"+" Hardware Capacity :"+me.getValue().getHardDiskCapacity()+"\t"+" Average CPU Utlization: "+me.getValue().getAvgCPUtilization());
                    }

【讨论】:

    【解决方案4】:

    你的问题是合并两个 POJO 类。 例如

    class Server {
    
        private String ipAddr;
        private String hw1;
        private String hw2;
        private String hw3;
        //Getter and Setters
    }
    
    Server s1 = new Server("0A", "a1", null, null);
    Server s2 = new Server("0A", null, "b2", null);
    

    所以合并后的pojo应该是这样的。

    Server merged = merge(s1, s2);// Server{ipAddr=0A, hw1=a1, hw2=b2, hw3=null}
    

    合并函数长这样……

    public static Server merge(Server s1, Server s2) throws Exception {
    
        Server merged = new Server();
    
        for (Field field : Server.class.getDeclaredFields()) {
            field.setAccessible(true);
            Object getS1 = field.get(s1);
            Object getS2 = field.get(s2);
    
            if(getS1 == null && getS2 != null) {
                field.set(merged, getS2);
            } else if (getS1 != null && getS2 == null) {
                field.set(merged, getS1);
            } else {  //equal values
                field.set(merged, getS1);
            }
        }
    
        return merged;
    }
    

    这是合并三个地图的示例代码,它有点快速和肮脏但效果很好。

    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    class MergeMaps {
    
    public static void main(String[] args) throws Exception {
    
        Map<String, Server> map1 = new HashMap<>();
        Map<String, Server> map2 = new HashMap<>();
        Map<String, Server> map3 = new HashMap<>();
    
        // Add some test data to map 1
        map1.put("serv1", new Server("0A", "a1", null, null));
        map1.put("serv2", new Server("0B", "b1", null, null));
        System.out.println(map1);
    
        // Add some data to map 2
        map2.put("serv1", new Server("0A", null, "a2", null));
        map2.put("serv2", new Server("0B", null, "b2", null));
        map2.put("serv3", new Server("0C", null, "c2", null));
        System.out.println(map2);
    
        // Add some data to map 3
        map3.put("serv1", new Server("0A", null, null, "a3"));
        map3.put("serv2", new Server("0B", null, null, "b3"));
        map3.put("serv3", new Server("0C", null, null, "c3"));
        map3.put("serv4", new Server("0D", null, null, "d4"));
        System.out.println(map3);
    
        Map<String, Server> resultingMap = new HashMap<>();
    
        resultingMap.putAll(map1);
    
        for (Map.Entry<String, Server> entry : map2.entrySet()) {
            if (resultingMap.containsKey(entry.getKey())) {
                Server s = resultingMap.get(entry.getKey());
                Server t = entry.getValue();
    
                Server merged = merge(s, t);
                resultingMap.put(entry.getKey(), merged);
            } else {
                resultingMap.put(entry.getKey(), entry.getValue());
            }
        }
    
        for (Map.Entry<String, Server> entry : map3.entrySet()) {
            if (resultingMap.containsKey(entry.getKey())) {
                Server server1 = resultingMap.get(entry.getKey());
                Server server2 = entry.getValue();
    
                Server merged = merge(server1, server2);
                resultingMap.put(entry.getKey(), merged);
            } else {
                resultingMap.put(entry.getKey(), entry.getValue());
            }
        }
        System.out.println(resultingMap);
    }
    
    public static Server merge(Server s1, Server s2) throws Exception {
    
        Server merged = new Server();
    
        for (Field field : Server.class.getDeclaredFields()) {
            field.setAccessible(true);
            Object getS1 = field.get(s1);
            Object getS2 = field.get(s2);
    
            if (getS1 == null && getS2 != null) {
                field.set(merged, getS2);
            } else if (getS1 != null && getS2 == null) {
                field.set(merged, getS1);
            } else {
                field.set(merged, getS1);
            }
        }
    
        return merged;
    }
    }
    
    class Server {
    
        private String ipAddr;
        private String hw1;
        private String hw2;
        private String hw3;
    
        public Server() {
        }
    
        public Server(String ipAddr, String hw1, String hw2, String hw3) {
            this.ipAddr = ipAddr;
            this.hw1 = hw1;
            this.hw2 = hw2;
            this.hw3 = hw3;
        }
        //Getter and setters
        @Override
        public String toString() {
            return "Server{" + "ipAddr=" + ipAddr + ", hw1=" + hw1 + ", hw2=" + hw2 +     ", hw3=" + hw3 + '}';
    }
    }
    

    输出看起来像这样..

    {serv2=Server{ipAddr=0B, hw1=b1, hw2=null, hw3=null}, serv1=Server{ipAddr=0A, hw1=a1, hw2=null, hw3=null}}
    {serv2=Server{ipAddr=0B, hw1=null, hw2=b2, hw3=null}, serv3=Server{ipAddr=0C, hw1=null, hw2=c2, hw3=null}, serv1=Server{ipAddr=0A, hw1=null, hw2=a2, hw3=null}}
    {serv2=Server{ipAddr=0B, hw1=null, hw2=null, hw3=b3}, serv3=Server{ipAddr=0C, hw1=null, hw2=null, hw3=c3}, serv4=Server{ipAddr=0D, hw1=null, hw2=null, hw3=d4}, serv1=Server{ipAddr=0A, hw1=null, hw2=null, hw3=a3}}
    {serv2=Server{ipAddr=0B, hw1=b1, hw2=b2, hw3=b3}, serv3=Server{ipAddr=0C, hw1=null, hw2=c2, hw3=c3}, serv4=Server{ipAddr=0D, hw1=null, hw2=null, hw3=d4}, serv1=Server{ipAddr=0A, hw1=a1, hw2=a2, hw3=a3}}
    

    【讨论】:

    • 这假定 POJO 中的字段是 public。他们不是。
    • @BoristheSpider 感谢您指出这一点。我也更新了合并私有字段的代码。
    • 我认为绕过访问器直接戳private字段并不能真正解决问题。我的建议是删除这个自制软件并使用known working solution
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-06
    • 2021-04-25
    • 1970-01-01
    • 2017-08-05
    • 2014-06-08
    相关资源
    最近更新 更多