如何使用Java HashMap和IdentityHashMap?

时间:2012-11-29 17:39:22

标签: java

我正在使用HashMap。

以下是示例代码。

类MyKey和MyValue以简单的方式来自Object。

Java文档说对象和方法hashCode()和equals():

“尽可能合理地定义了hashCode方法 by class Object确实为不同的对象返回不同的整数。 (这通常通过转换内部地址来实现 将对象转换为整数,但这种实现技术 JavaTM编程语言不要求它。)“

“类Object的equals方法实现最具辨别力 对象可能的等价关系;也就是说,对于任何非空引用 值x和y,当且仅当x和y引用时,此方法返回true 到同一个对象(x == y的值为true)。 “

我的问题是:

我能相信HashMap在我的例子中有用吗? 如果不是在没有重写方法的情况下将简单对象放入地图的正确方法 hashCode()和equals()?

我不确定,但我听说过Java可能会改变位置和地址 程序执行期间的用户对象(GC可能会这样做吗?)

如果key2的地址和哈希码在行

之前发生了变化
    MyValue v = m.get(key2);

然后调用m.get(key2)将返回错误的值,null?

如果这是真的那么我相信IdentityHashMap() 因同样的原因没用。

class MyKey
{
  Integer v;
  //<Perhaps more fields>
  MyKey(Integer v) {this.v=v;}
}

class MyValue
{
  String s;
  //<Perhaps more fields>
  MyValue(String s) {this.s = s;}
}


Then some code:

Map<MyKey,MyValue> m = new HashMap<MyKey,MyValue>();

MyKey key1 = new MyKey(5);
MyKey key2 = new MyKey(6);
MyKey key3 = new MyKey(7);


m.put(key1, new MyValue("AAA"));
m.put(key2, new MyValue("BBB"));
m.put(key3, new MyValue("CCC"));

.
.

//Is it sure that I will get value "AAA" here
//if entry with key2 has not been removed from map m?
MyValue v = m.get(key2);
System.out.println("s="+v.s);

3 个答案:

答案 0 :(得分:2)

  

我能相信HashMap在我的例子中有用吗?如果没有将简单对象放入地图而没有重写方法hashCode()和equals()的正确方法?

您无法避免提供合理的hashCode和equals方法,HashMap和其他Hash集合需要它才能工作。 (IdentityHashMap除外)

  

我不确定,但我听说Java可能会在程序执行期间更改用户对象的位置和地址(GC可能会这样做吗?)

虽然这是真的,但它与你的主要问题无关。

  

如果key2的地址和哈希码在行

之前发生了变化

地址和hashCode彼此无关。如果地址发生更改,则不会更改hashCode,如果更改hashCode,则不会更改地址。

  

如果这是真的那么我相信IdentityHashMap()也因为同样的原因而无用。

即使你认为hashCode没用,这也不会影响IndentityHashCode,因为它不使用hashCodeequals方法。


对象基本上是从Eden空间在内存中连续分配的。如果你运行

Object[] objects = new Object[20];
for (int i = 0; i < objects.length; i++)
    objects[i] = new Object();

Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
for (int i = 0; i < objects.length; i++) {
    int location = unsafe.getInt(objects, Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * i);
    System.out.println(Integer.toHexString(location) + ": hashCode=" + Integer.toHexString(objects[i].hashCode()));
}

如果跟随一些内存位置但是它们不是

,你可能会认为它们是连续的
eac89d10: hashCode=634e3372
eac89d20: hashCode=2313b44d
eac89d30: hashCode=62a23d38
eac89d40: hashCode=9615a1f
eac89d50: hashCode=233aa44
eac89d60: hashCode=59243f75
eac89d70: hashCode=5ac2480b
eac89d80: hashCode=907f8ba
eac89d90: hashCode=6a5a7ff7
eac89da0: hashCode=5b8767ad
eac89db0: hashCode=50ba0dfc
eac89dc0: hashCode=2198a037
eac89dd0: hashCode=2b3e8c1c
eac89de0: hashCode=17609872
eac89df0: hashCode=46b8705b
eac89e00: hashCode=76d88aa2
eac89e10: hashCode=275cea3
eac89e20: hashCode=4513098
eac89e30: hashCode=6e4d4d5e
eac89e40: hashCode=15128ee5

Java在32位和64位中有四种不同的编码引用方式,但是如果你的最大堆大小小于2 GB,它将是一个简单的32位地址,就像我运行这个例子时一样。 / p>

答案 1 :(得分:1)

  

我能相信HashMap在我的例子中有用吗?如果没有将简单对象放入地图而没有重写方法hashCode()和equals()的正确方法?

您的示例不提供hashCode或equals,因此它将使用默认值。 dafaults使用对象标识,这意味着仅当o和o2引用同一个对象时,o.equals(o2)才为真。

 MyKey m = new MyKey(1);
 MyKey m2 = new MyKey(1);
 MyKey m3 = m;

 map.put(m,...);
 map.get(m);//works
 map.get(m2); //different object
 map.get(m3);//works same object
  

我不确定,但我听说Java可能会在程序执行期间更改用户对象的位置和地址(GC可能会这样做吗?)

对象的地址不相关,而默认的hashCode可能会使用它,这对每个对象只发生一次,然后保持不变。

答案 2 :(得分:0)

首先,对象的默认哈希码在构造后不会改变,因此IdentityHashMap不会无用。 Java可能会在内存中移动对象,但它不会更改其标识哈希码。

其次,如果您没有定义自己的equals()方法,那么您构建的每个对象都不会equals()。这意味着如果您想将它们用作HashMap(或IdentityHashMap)中的键,您将只能使用原始对象来检索它们。

例如:

MyKey key = new MyKey(5);
m.put(key, value);
...
MyKey newKey = new MyKey(5);
m.get(newKey); // Will not find the value

由于newKeykey是不同的对象,因此它们不是==。这就是为什么建议您为对象覆盖equals()(和hashcode())。

如果您没有覆盖对象上的HashMapequals(),但hashcode()仍然有效,但通常不会执行您想要的操作 - 在这种情况下,它等同于{ {1}}

相关问题