1

当我写

String str1 = new String("hello");
String str2 = new String("hello");

afaik,即使字符串内容相同,str1并且str2将指向单独的内存位置,因为它将在堆中而不是在池中引用。

在这种情况下,当我执行下面的程序时,为什么当字符串的两个实例(键)不同时它会给我一个值?那么为什么用户定义的对象没有发生同样的情况呢?

String str1 = new String("hello");
String str2 = new String("hello");
AB obj1 = new AB();
BC obj2 = new BC();

HashMap h = new HashMap();
h.put(str1, "data");
h.put(obj1, "data1");

System.out.println(h.get(str2));
System.out.println(h.get(obj2));

class AB {
  int code = 10;
}

class BC {
  int code = 10;
}

输出:

data
null
4

6 回答 6

1

因为 HashMap 用于equals比较键。尽管您的 2 个字符串是不同的实例,但它们是相等的。

您的自定义对象不能相等,因为它们甚至不是同一类型。即使它们属于同一类型,除非您重写从equals继承的方法Object,否则2 个不同的实例(使用 创建new)将不相等。

请注意,如果您覆盖equals,您还需要覆盖hashCode以避免意外行为。

于 2012-10-22T10:38:57.590 回答
1

HashMap.get() 函数调用 Object.hashCode() 方法来创建键值。但是,String 类会覆盖此方法,并根据值进行计算。如果我们覆盖这些(AB 和 BC)类的 hashCode 函数,我们会找到相同的解决方案。

字符串哈希码():

public int hashCode() {
int h = hash;
    int len = count;
if (h == 0 && len > 0) {
    int off = offset;
    char val[] = value; // get value of string.

        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

这是 HashMap.get() 方法

public V get(Object key) {
    if (key == null)
        return getForNullKey();
    int hash = hash(key.hashCode());// it calculate at here.
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
            return e.value;
    }
    return null;
}
于 2012-10-22T10:48:24.880 回答
0

在使用某些对象作为哈希映射中的键时达成了一项协议,
您现在必须覆盖哈希码和等于方法,因为 String 类会这样做 ,如果您以所需的方式覆盖 AB 和 BC 的哈希码和等于方法,您
将从第一个 sysout 获取“数据”,您将
从第二个 sysout 获取 data1

并且 str1 和 str2不会指向单独的内存位置

于 2012-10-22T10:40:16.827 回答
0

因为HashMap会首先根据字符串对象的hashcode找到正确的bucket。一旦找到bucket,之后会调用Equals方法来比较key。

Ps 请注意,Equals 比较被 String 类覆盖,因此它不仅根据引用进行比较,而且根据内容进行比较。

于 2012-10-22T10:40:59.010 回答
0

比较Strings 的内容。不是实际的对象或参考。

由于str1str2都具有相同的内容,因此它为您提供了HashMap.

HashMap 使用equals方法来搜索其中的确切键。由于str1.equals(str2)results in true,因此找到了键,因此get方法返回了值。

尝试如下编辑您的课程,您会感到惊讶:

class AB 
{   
    int code = 10; 
    public boolean equals(Object argument)
    {
        if(argument instanceof AB || argument instanceof BC)
            return true;
        return false;
    }
}  

class BC 
{   
    int code = 10; 
    public boolean equals(Object argument)
    {
        if(argument instanceof AB || argument instanceof BC)
            return true;
        return false;
    }
}

现在,您的第二条System.out.println(h.get(obj2));语句也将返回它的映射数据。

于 2012-10-22T10:41:06.200 回答
0

map 的实现方式是利用类的 hashCode 和 equals 方法。

首先,当您将类的实例添加到地图时,地图的实现会将该实例放入存储桶中。这就是为什么正确实现 hashCode 以确保映射中的存储桶有效分布的重要原因。

使用equals方法是为了判断bucket中的哪个实例是你要的实例,所以正确实现equals很重要。

在您的情况下, String 实现了 hashCode 和 equals 正确,而您的用户定义的对象不是同一类型,因此不能相等(无论如何您都不会覆盖 equals 或 hashCode)。

于 2012-10-22T10:47:23.420 回答