简体   繁体   English

如何使用 Java HashMap 和 IdentityHashMap?

[英]How to use Java HashMap and IdentityHashMap?

I am using HashMap.我正在使用 HashMap。

Below is example code.下面是示例代码。

Classes MyKey and MyValue are inhereted from Object in simple way.类 MyKey 和 MyValue 以简单的方式从 Object 继承。

Java documentation says for Object and methods hashCode() and equals(): Java 文档说明了对象和方法 hashCode() 和 equals():

"As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)" “在合理可行的情况下,类 Object 定义的 hashCode 方法确实为不同的对象返回不同的整数。(这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 不需要这种实现技术编程语言。)”

"The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true). " “类 Object 的 equals 方法在对象上实现了最有区别的可能等价关系;也就是说,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象(x = = y 的值为 true)。”

My question is:我的问题是:

Can I trust that HashMap works in my example?我可以相信 HashMap 在我的示例中有效吗? If not what would be right way to put simple objects into map without rewriting methods hashCode() and equals()?如果不是,在不重写方法 hashCode() 和 equals() 的情况下将简单对象放入 map 的正确方法是什么?

I am not sure but I have heard that Java may change location and addresss of user objects during execution of program (Was it GC which may do that?)我不确定,但我听说 Java 可能会在程序执行期间更改用户对象的位置和地址(是 GC 可能会这样做吗?)

If address and hash code of key2 have changed before the line如果key2的地址和哈希码在该行之前发生了变化

    MyValue v = m.get(key2);

then calling m.get(key2) would return wrong value, null?然后调用 m.get(key2) 会返回错误的值,null?

If this is true then I believe that also IdentityHashMap() is useless for same reasons.如果这是真的,那么我相信 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);

Can I trust that HashMap works in my example?我可以相信 HashMap 在我的示例中有效吗? If not what would be right way to put simple objects into map without rewriting methods hashCode() and equals()?如果不是,在不重写方法 hashCode() 和 equals() 的情况下将简单对象放入 map 的正确方法是什么?

You cannot avoid providing a sensible hashCode and equals methods, it is required for HashMap and other Hash collections to work.您无法避免提供合理的 hashCode 和 equals 方法,它是 HashMap 和其他 Hash 集合工作所必需的。 (with the exception of IdentityHashMap) (IdentityHashMap 除外)

I am not sure but I have heard that Java may change location and addresss of user objects during execution of program (Was it GC which may do that?)我不确定,但我听说 Java 可能会在程序执行期间更改用户对象的位置和地址(是 GC 可能会这样做吗?)

While this is true, it has nothing to do with your main question.虽然这是真的,但它与您的主要问题无关。

If address and hash code of key2 have changed before the line如果key2的地址和哈希码在该行之前发生了变化

The address and hashCode have nothing to do with one another.地址和 hashCode 没有任何关系。 If the address changes it doesn't change the hashCode and if you change the hashCode it doesn't change the address.如果地址改变,它不会改变hashCode,如果你改变hashCode,它不会改变地址。

If this is true then I believe that also IdentityHashMap() is useless for same reasons.如果这是真的,那么我相信 IdentityHashMap() 出于同样的原因也无用。

Even if you assume hashCode is useless, this doesn't affect IndentityHashCode because it doesn't use the hashCode or equals methods.即使您假设 hashCode 无用,这也不会影响 IndentityHashCode,因为它不使用hashCodeequals方法。


Objects are basically allocated continuously in memory from the Eden space.对象基本上是从 Eden 空间连续分配到内存中的。 If you run如果你跑

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

you might expect them to be continuous if the followed some memory location but they don't如果遵循某个内存位置,您可能希望它们是连续的,但它们不是

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 has four different way of encoding references in 32-bit and 64-bit, however if your maximum heap size is less than 2 GB it will be a simple 32-bit address as it was when I ran this example. Java 有四种不同的 32 位和 64 位编码引用方式,但是如果您的最大堆大小小于 2 GB,它将是一个简单的 32 位地址,就像我运行这个例子时一样。

Can I trust that HashMap works in my example?我可以相信 HashMap 在我的示例中有效吗? If not what would be right way to put simple objects into map without rewriting methods hashCode() and equals()?如果不是,在不重写方法 hashCode() 和 equals() 的情况下将简单对象放入 map 的正确方法是什么?

Your example does not provide hashCode or equals, so it will use the defaults.您的示例不提供 hashCode 或 equals,因此它将使用默认值。 The dafaults work with the object identity, which means o.equals(o2) will be true only if o and o2 refer to the same object. dafaults 使用对象标识,这意味着 o.equals(o2) 只有当 o 和 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

I am not sure but I have heard that Java may change location and addresss of user objects during execution of program (Was it GC which may do that?)我不确定,但我听说 Java 可能会在程序执行期间更改用户对象的位置和地址(是 GC 可能会这样做吗?)

The address of an object is not relevant, while the default hashCode might use it this happens only once for each object and then stays the same.对象的地址是不相关的,而默认的 hashCode 可能会使用它,这对于每个对象只发生一次,然后保持不变。

First off, the default hashcode of an object does not change after it is constructed so IdentityHashMap is not useless.首先,对象的默认哈希码在构造后不会更改,因此 IdentityHashMap 不是无用的。 Java may move objects around in memory but it doesn't change their identity hash code. Java 可能会在内存中移动对象,但不会更改它们的身份哈希码。

Secondly, if you don't define your own equals() method, then every object you construct will be not equals() to every other.其次,如果您没有定义自己的equals()方法,那么您构造的每个对象都不会与其他对象equals() This means if you want to use them as keys in a HashMap (or IdentityHashMap) you'll only be able to retrieve them by using the original object.这意味着如果您想将它们用作 HashMap(或 IdentityHashMap)中的键,您将只能使用原始对象来检索它们。

For example:例如:

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

As newKey and key are different objects, they are not == .由于newKeykey是不同的对象,它们不是== This is why you are recommended to override equals() (and hashcode() ) for you objects.这就是为什么建议您为您的对象覆盖equals() (和hashcode() )的原因。

A HashMap still works if you don't override equals() and hashcode() on your objects but it will generally not do what you want - in this case, it becomes equivalent to IdentityHashMap如果您不覆盖对象上的equals()hashcode()HashMap仍然有效,但它通常不会执行您想要的操作 - 在这种情况下,它等效于IdentityHashMap

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM