我正在使用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);
答案 0 :(得分:2)
我能相信HashMap在我的例子中有用吗?如果没有将简单对象放入地图而没有重写方法hashCode()和equals()的正确方法?
您无法避免提供合理的hashCode和equals方法,HashMap和其他Hash集合需要它才能工作。 (IdentityHashMap除外)
我不确定,但我听说Java可能会在程序执行期间更改用户对象的位置和地址(GC可能会这样做吗?)
虽然这是真的,但它与你的主要问题无关。
如果key2的地址和哈希码在行
之前发生了变化
地址和hashCode彼此无关。如果地址发生更改,则不会更改hashCode,如果更改hashCode,则不会更改地址。
如果这是真的那么我相信IdentityHashMap()也因为同样的原因而无用。
即使你认为hashCode没用,这也不会影响IndentityHashCode,因为它不使用hashCode
或equals
方法。
对象基本上是从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
由于newKey
和key
是不同的对象,因此它们不是==
。这就是为什么建议您为对象覆盖equals()
(和hashcode()
)。
如果您没有覆盖对象上的HashMap
和equals()
,但hashcode()
仍然有效,但通常不会执行您想要的操作 - 在这种情况下,它等同于{ {1}}