在HashMap中反转键值对

时间:2017-03-30 01:44:34

标签: java arraylist hashmap

我的数据结构如下:

Map<String,ArrayList<String>> graph = new HashMap<String,ArrayList<String>>();

这实际上是一个哈希映射,它将字符串值作为键,并将字符串数组列表存储在键的值中。 现在我试图反转键值模式,使值成为键并键入值。我这样做的方式如下:

private Map<String,ArrayList<String>> reverseAdjList(Map<String,ArrayList<String>> adjList){
    Map<String,ArrayList<String>> tGraph = new HashMap<String,ArrayList<String>>();
    for (Map.Entry<String, ArrayList<String>> entry : adjList.entrySet()) {
        String key = entry.getKey();
        ArrayList<String> values = new ArrayList<>();
        values.add(key);
        ArrayList<String> value = entry.getValue();    
        for(String v:value){
            if(tGraph.containsKey(v)){
                values.addAll(tGraph.get(v));
            }
            tGraph.put(v, values);
        }
    }
    return tGraph;
}

所以这对我来说可以反转小数据集的哈希映射键值模式,但是当我在更大的数据集上尝试它时,我遇到了

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.addAll(ArrayList.java:579)
at GraphProcessor.reverseAdjList(GraphProcessor.java:67)
at GraphProcessor.SCC(GraphProcessor.java:135)
at GraphProcessor.<init>(GraphProcessor.java:50)
at GraphProcessor.main(GraphProcessor.java:250)

我知道这是一种非常天真和错误的做法,这是一种更好,更正确的方法吗?

1 个答案:

答案 0 :(得分:4)

您的代码中存在错误:

for (Map.Entry<String, ArrayList<String>> entry : adjList.entrySet()) {
    String key = entry.getKey();
    ArrayList<String> values = new ArrayList<>(); // Wrong place for this variable.
    values.add(key);
    ArrayList<String> value = entry.getValue();    
    for(String v:value){
        if(tGraph.containsKey(v)){
            values.addAll(tGraph.get(v));
        }
        tGraph.put(v, values);
    }
}

本地变量values应该在嵌套的for循环中,否则values会为所有以后的新密钥v累积,并且如果你的话,将耗费大量内存数据集很大,应该是:

private Map<String, ArrayList<String>> reverseAdjList(Map<String, List<String>> adjList) {
    Map<String, ArrayList<String>> tGraph = new HashMap<>();
    for (Map.Entry<String, List<String>> entry : adjList.entrySet())  {
        String key = entry.getKey();
        List<String> value = entry.getValue();
        for (String v : value) {
            ArrayList<String> values = new ArrayList<>();
            values.add(key);
            if (tGraph.containsKey(v)) {
                values.addAll(tGraph.get(v));
            }
            tGraph.put(v, values);
        }
    }
    return tGraph;
}

但实际上您并不需要为每个内部for步骤创建一个新的List实例,请使用JDK 1.8尝试以下代码:

private  Map<String, List<String>> reverseMap(Map<String, List<String>> adjList) {
    Map<String, List<String>> tGraph = new HashMap<>();
    for (Map.Entry<String, List<String>> entry : adjList.entrySet()) {
        for (String value : entry.getValue()) {
            tGraph.computeIfAbsent(value, v -> new ArrayList<>()).add(entry.getKey()); // Updated according comment from @shmosel
        }
    }
    return tGraph;
}

如果您使用旧版本的jdk,可以尝试:

    private Map<String, List<String>> reverseMap(Map<String, List<String>> adjList) {
    Map<String, List<String>> tGraph = new HashMap<>();
    for (Map.Entry<String, List<String>> entry : adjList.entrySet()) {
        for (String value : entry.getValue()) {
            List<String> newValues = tGraph.get(value);
            if (newValues == null) {
                newValues = new ArrayList<>();
                tGraph.put(value, newValues);
            }
            newValues.add(entry.getKey());
        }
    }
    return tGraph;
}

希望这可能会有所帮助: - )