java HashMap.putall克隆所有元素吗?

时间:2015-04-08 14:01:34

标签: java hashmap cloning

我有一个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确实创建副本。我做错了什么?

5 个答案:

答案 0 :(得分:4)

来自documentation

  

<强>的putAll

     

将指定地图中的所有映射复制到此地图(可选操作)。此调用的效果等同于在指定映射中从键k到值v的每个映射在此映射上调用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]

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

如您所见,没有副本。

 /**
 * 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());
    }

我们只是将每个Key-Value放到我们的地图上。

答案 4 :(得分:0)

Java中没有深层副本。如果你想要它,你必须通过递归调用clone()来编码它。

另请注意,数组是

中的int[]对象
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
    }