java中的地图问题

时间:2011-08-05 20:50:04

标签: java collections hashmap

我有一个Hashmap,它有X个元素
我需要将此地图移动到另一个地图中 这就是我的代码看起来像

Map originMap = initialize();
Map destMap = new Hashmap ();  

int originMapSize = originMap.size(); 
Set<Map.Entry<K, V>> entries = originMap.entrySet();
for (Map.Entry<K, Y> mapEntry : entries) {
 K key = mapEntry.getKey();
 V value = mapEntry.getValue();
 destMap.put (key,value);
}  

// Shouldnt this be equal to originMapSize ????
int destMapSize = destMap.size();

我观察的是 - originMapSize不等于destMapSize

似乎当我们将元素放在destMap中时,一些元素被覆盖

我们已经覆盖了hashCode和equals方法 - 这是一个可疑的实现 但是,如果originMap允许添加元素,为什么destinationMap不会添加新元素而是覆盖现有元素呢?

6 个答案:

答案 0 :(得分:8)

如果equals方法不对称,可能会发生这种情况。假设有两个键a和b,使得:

  • a.hashCode() == b.hashCode()
  • a.equals(b)返回false
  • b.equals(a)返回true

然后假设HashMap实现通过使用与新密钥相同的哈希码为每个现有密钥调用existingKey.equals(newKey)来搜索现有密钥。

现在假设我们最初按照{a,b}的顺序添加它们。

第一个键(a)显然没有任何问题。第二个密钥(b)插入最终调用a.equals(b) - 这是假的,因此我们得到两个密钥。

现在构建第二个HashMap,我们最终可能会按顺序{b,a}获取条目。

这次我们首先添加b,这很好......但是当我们插入第二个键(a)时,我们最终调用b.equals(a),返回true,所以我们覆盖条目。

这可能不是正在发生的事情,但可以解释事情 - 并显示非对称equals方法的危险。

编辑:这是一个简短但完整的程序,展示了这种情况。 (ab的具体细节可能不一样,但不对称是。)

import java.util.*;

public class Test {

    private final String name;

    public Test(String name)
    {
        this.name = name;
    }

    public static void main(String[] args)
    {
        Map<Test, String> firstMap = new HashMap<Test, String>();

        Test a = new Test("a");
        Test b = new Test("b");

        firstMap.put(b, "b");
        firstMap.put(a, "a");

        Map<Test, String> secondMap = new HashMap<Test, String>();
        for (Map.Entry<Test, String> entry : firstMap.entrySet())
        {
            System.out.println("Adding " + entry.getKey().name);
            secondMap.put(entry.getKey(), entry.getValue());
        }
        System.out.println(secondMap.size());
    }

    @Override public int hashCode()
    {
        return 0;
    }

    @Override public boolean equals(Object other)
    {
        return this.name.equals("b");
    }
}

我机器上的输出:

Adding a
Adding b
1

你可能无法获得输出 - 这取决于:

  • 调用equals的方式(candidateKey.equals(newKey)或反之亦然)
  • 从集合
  • 返回条目的顺序

在不同的跑步中它甚至可能有不同的作用。

答案 1 :(得分:0)

这些值应该相等,但问题是你在迭代不同的Map对象。

for (Map.Entry mapEntry : entries)

不同
for (Map.Entry mapEntry : originMap)

答案 2 :(得分:0)

我怀疑添加到第一个hashmap的元素的顺序与添加到第二个hashmap的顺序不一样。这与粗略的hashCode方法相结合,导致重复项被添加到第一个。

尝试更改hashCode以始终返回相同的值,以查看问题是否消失。

答案 3 :(得分:0)

为什么不使用destMap.putAll(originMap)?

答案 4 :(得分:0)

Map有一个 putAll 方法。尝试这样的事情:

    Map<String, String> destination = new HashMap<String, String>();
    Map<String, String> original = new HashMap<String, String>();

    destination.putAll(original);

答案 5 :(得分:0)

这取决于第一个HashMap的初始化方式。每次将对象添加到HashMap中时,一旦它通过75%的加载因子,它就会分配两倍的默认大小来容纳新值。地图通常具有默认大小= 16:当您通过75%的加载因子时,它会放大到32。