【问题标题】:does java HashMap.putall clone all elements?java HashMap.putall 是否克隆所有元素?
【发布时间】:2015-06-13 13:01:21
【问题描述】:

我有一个 Hashmap,我在其中编写了一个处理添加和检索值的类。

class ReputationMatrix
{
    private HashMap < Integer, int[] > repMatrix;

    public ReputationMatrix()
    {
        repMatrix = new HashMap < Integer, int[] > ();
    }

    public void addrating(int nodeId, boolean rating)
    {
        int[] alphaBeta;

        if (repMatrix.containsKey(nodeId))
        {
            alphaBeta = repMatrix.get(nodeId);

            if (rating == true)
            {
                alphaBeta[0] = alphaBeta[0] + 1;
            }
            else
            {
                alphaBeta[1] = alphaBeta[1] + 1;
            }

            repMatrix.put(nodeId, alphaBeta);
        }
        else
        {
            alphaBeta = new int[2];

            if (rating == true)
            {
                alphaBeta[0] = 2;
                alphaBeta[1] = 1;
            }
            else
            {
                alphaBeta[0] = 1;
                alphaBeta[1] = 2;
            }

            repMatrix.put(nodeId, alphaBeta);

        }
    }

    public int[] getNodeIds()
    {
        int[] nodeIds = new int[repMatrix.size()];
        int index = 0;

        for (int key: repMatrix.keySet())
        {
            nodeIds[index] = key;
            index++;
        }

        return nodeIds;
    }

    public int getAlpha(int nodeId)
    {
        return repMatrix.get(nodeId)[0];
    }

    public int getBeta(int nodeId)
    {
        return repMatrix.get(nodeId)[1];
    }

    public ReputationMatrix clone()
    {
        ReputationMatrix matrixClone = new ReputationMatrix();
        matrixClone.repMatrix.putAll(this.repMatrix);
        return matrixClone;
    }
}

我实现了一个克隆方法来简单地返回一个完全独立于原始的 ReputationMatrix 的单独副本。

我测试了这样的代码:

public class Main
{
    /**
     * @param args
     */
    public static void main(String[] args)
    {
        ReputationMatrix matrix1 = new ReputationMatrix();
        matrix1.addrating(18, true);

        ReputationMatrix matrix2 = matrix1.clone();

        System.out.println(matrix1.getAlpha(18));
        System.out.println(matrix2.getAlpha(18));

        matrix1.addrating(18, true);

        System.out.println(matrix1.getAlpha(18));
        System.out.println(matrix2.getAlpha(18));
    }
}

输出是:

2
2
3
3

这意味着我对 matrix1 应用的每一个更改都会反映在 matrix2 上。 我几乎可以肯定 putAll 确实会创建副本。我做错了什么?

【问题讨论】:

  • .putAll() 从不做“深拷贝”;它只做浅拷贝。你从哪里读到它做了深拷贝?
  • put 和 putall 只是存储对对象的引用。
  • 也许我错了。所以我可以克隆 Hashmap 的唯一方法是遍历所有键并克隆数组?
  • 在 Java 中你永远不会自动获得副本,它必须是显式的。有一个根本原因:复制对象图是一个定义不明确的概念,一般情况下无法解决。

标签: java hashmap cloning


【解决方案1】:

来自documentation

putAll

将所有映射从指定映射复制到此映射(可选操作)。这个调用的效果等价于对指定map中键k到值v的每一次映射在这个map上调用put(k, v)一次。

所以它不会复制对象,它只会将原始地图的映射添加到新地图。

要做你想做的事,你需要明确地复制每个值:

Map<Integer, int[]> originalMatrix = new HashMap<>();
int[] original = {1, 2, 3};
originalMatrix.put(1, original);
Map<Integer, int[]> newMatrix = new HashMap<>();
    
for (Map.Entry<Integer, int[]> entry : originalMatrix.entrySet()) {
    newMatrix.put(entry.getKey(), entry.getValue().clone());
}

Arrays.fill(original, 0);

System.out.println(Arrays.toString(original));
System.out.println(Arrays.toString(newMatrix.get(1)));

输出:

[0, 0, 0]
[1, 2, 3]

【讨论】:

  • 我有以下数据集HashMap &lt;Integer, HashMap&lt;Integer,List&lt;Float&gt;&gt;&gt;,当我使用 putAll 时,我看起来像是复制的对象而不是值。因为第一个的变化会影响第二个
【解决方案2】:

putAll 不会创建键和值的副本。它为传递给它的每个键/值对调用put,而put 不会创建副本。

【讨论】:

    【解决方案3】:

    不,putAll() 将元素克隆到地图。它只是复制对它们的引用,因此您有两个指向同一个对象的引用变量到堆中。这称为复制。如果您想克隆所有元素(deep 复制),您必须执行以下操作:

    Map<K,V> original = new HashMap<K,V>();
    Map<K,V> clone = new HashMap<K,V>();
    for(Map.Entry<K,V> entry : original.entrySet) {
      clone.put(entry.getKey(), entry.getValue().clone());
    }
    

    【讨论】:

    • 你的意思是 entry.get[Key|Value].clone(),是吗?
    • 是的!感谢您的关注:)我只需要克隆值,与实际上使用的密钥哈希具有相同的密钥是没有问题的。
    【解决方案4】:

    如您所见,没有副本。

     /**
     * Copies all of the mappings from the specified map to this map.
     * These mappings will replace any mappings that this map had for
     * any of the keys currently in the specified map.
     *
     * @param m mappings to be stored in this map
     * @throws NullPointerException if the specified map is null
     */
    public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;
    
        /*
         * Expand the map if the map if the number of mappings to be added
         * is greater than or equal to threshold.  This is conservative; the
         * obvious condition is (m.size() + size) >= threshold, but this
         * condition could result in a map with twice the appropriate capacity,
         * if the keys to be added overlap with the keys already in this map.
         * By using the conservative calculation, we subject ourself
         * to at most one extra resize.
         */
        if (numKeysToBeAdded > threshold) {
            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }
    
        for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<? extends K, ? extends V> e = i.next();
            put(e.getKey(), e.getValue());
        }
    } 
    

    这是来自openjdk的原始src。

     for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<? extends K, ? extends V> e = i.next();
            put(e.getKey(), e.getValue());
        }
    

    我们只是将每个键值对我们的地图。

    【讨论】:

    • 如果您解释了证明没有创建副本的代码部分,这将是一个很好的答案。
    【解决方案5】:

    Java 中没有深拷贝。如果你想要它,你必须通过递归调用clone() 对其进行编码。

    还要注意,数组是对象,因此 int[] in

    private HashMap < Integer, int[] > repMatrix;
    

    是对 int 数组的引用。当你在addRating()中得到这个数组时,hashmap仍然持有对数组的引用,修改后不需要put()

        if (repMatrix.containsKey(nodeId)) {
            alphaBeta = repMatrix.get(nodeId);
    
            if (rating == true) {
                alphaBeta[0] = alphaBeta[0] + 1;
            }
            else {
                alphaBeta[1] = alphaBeta[1] + 1;
            }
    
            //repMatrix.put(nodeId, alphaBeta); <= not needed
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-26
      • 1970-01-01
      • 2011-02-12
      • 2012-01-01
      • 1970-01-01
      相关资源
      最近更新 更多