偶然的一次机会看到了这个题目,有一个网上的朋友说在面试的时候面到了这道题目,没有能给出解释。这里就谈一谈自己的理解。
在《Java 编程思想》中有这么一句话:设计 hashCode() 时最重要的因素就是对同一个对象调用 hashCode() 都应该产生相同的值。String 类型的对象对这个条件有着很好的支持,因为 String 对象的 hashCode() 值是根据 String 对象的内容计算的,并不是根据对象的地址计算。
下面是 String 类源码中的 hashCode() 方法:String 对象底层是一个 final 修饰的 char 类型的数组,hashCode() 的计算是根据字符数组的每个元素进行计算的,所以内容相同的 String 对象会产生相同的散列码。
/**
* Returns a hash code for this string. The hash code for a
* {@code String} object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using {@code int} arithmetic, where {@code s[i]} is the
* <i>i</i>th character of the string, {@code n} is the length of
* the string, and {@code ^} indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return a hash code value for this object.
*/
public int hashCode() {
int h = hash; //private int hash; // Default to 0
if (h == 0 && value.length > 0) {
char val[] = value; //获得 String 对象底层的字符数组
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i]; //在计算的时候加的是 int 类型的 ascii 码
}
hash = h;
}
return h;
}
我们希望的是我们在用对象作为 key 时,我们在获取的时候也依然能够根据重新定义 key 获得对应的 value。就像下面这样,但是它却不能正常工作:这两个对象产生的散列码是不同的,所以在进行 equals() 判断的时候这两个对象被认定为是不同的对象,自然也就获取不到对应的 value。
如果你想获得 map 中存储的“123”,那么你只有把 test 对象作为 key 才能获取到,我们试图用一种简单的方法就能让它工作,但是这显然与我们的初衷相背驰。所以非 String 类型的数据类型在判断 key 相同时所需要的条件太过苛刻。
Test test = new Test();
Map<Test,String> map1 = new HashMap<>();
map1.put(test,"123");
System.out.print(map1.get(new Test())); //我们试图取出对应的 value
/**
* 输出
* null
*/
但是 String 类型的对象就不一样了,内容相同的两个 String 对象具有相同的散列码,并且经过 equals() 判断后返回值为 true,所以在进行查找的时候它可以正常工作。
String key = "key";
HashMap<String,Integer> map = new HashMap<>();
map.put(key,123);
System.out.println(map.get("key"));
/**
* 输出
* 123
*/
总结:在使用 String 类型的对象做 key 时我们可以只根据传入的字符串内容就能获得对应存在 map 中的 value 值,而非 String 类型的对象在获得对应的 value 时需要的条件太过苛刻,首先要保证散列码相同,并且经过 equals() 方法判断为 true 时才可以获得对应的 value。
PS:
如果你想把自定义的对象作为 key,那也是可以的,你只需要重写 hashCode() 方法与 equals() 方法即可,按照你自己的意愿定义散列码。这篇博文属于博主自己的对这个问题理解,如果哪里有不准确或者是错误的理解还请大家指正。