1.在java中每个类都默认继承Object类,该类除非声明继承其它类。Object类中有一个toString的方法。该方法返回的是该Java对象的内存地址经过哈希算法得出的int类型的值转换成十六进制。
接下来讲解一下String的hashCode()方法:
String类的hashcode值(哈希值)是如何计算得到的?具体实现?为了方便阅读,我来进行分步说明
static void hashcodeTest(){
String str = "yangcq";
// 第一步 = (int)'y'
// 第二步 = (31 * (int)'y') + (int)'a'
// 第三步 = 31 * ((31 * (int)'y') + (int)'a') + (int)'n'
// 第四步 = 31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g'
// 第五步 = 31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c'
// 第六步 = 31 * (31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c') + (int)'q'
// 上面的过程,也可以用下面的方式表示
// 第一步 = (int)'y'
// 第二步 = 31 * (第一步的计算结果) + (int)'a'
// 第三步 = 31 * (第二步的计算结果) + (int)'n'
// 第四步 = 31 * (第三步的计算结果) + (int)'g'
// 第五步 = 31 * (第四步的计算结果) + (int)'c'
// 第六步 = 31 * (第五步的计算结果) + (int)'q'
int hashcode = 31 * (31 * (31 * (31 * ((31 * (int)'y') + (int)'a') + (int)'n') + (int)'g') + (int)'c') + (int)'q';
System.out.println("yangcq的hashcode = " + hashcode); // yangcq的hashcode = -737879313
System.out.println("yangcq的hashcode = " + str.hashCode()); // yangcq的hashcode = -737879313
}
---------------------
作者:春秋战国程序猿
来源:CSDN
原文:https://blog.csdn.net/reggergdsg/article/details/53819293
版权声明:本文为博主原创文章,转载请附上博文链接!
相关资料出自:https://blog.csdn.net/reggergdsg/article/details/53819293
以下是java的string的hashCode()源码:(注:hash默认初始值为0, /** Cache the hash code for the string */
private int hash; // Default to 0 。value为字符串)
/**
* Returns a hash code for this string. The hash code for a
* <code>String</code> object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using <code>int</code> arithmetic, where <code>s[i]</code> is the
* <i>i</i>th character of the string, <code>n</code> is the length of
* the string, and <code>^</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;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
注意那个狗血的31这个系数为什么总是在里面乘来乘去?为什么不适用32或者其他数字?
大家都知道,计算机的乘法涉及到移位计算。当一个数乘以2时,就直接拿该数左移一位即可!选择31原因是因为31是一个素数!
所谓素数:
质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。
在存储数据计算hash地址的时候,我们希望尽量减少有同样的hash地址,所谓“冲突”。如果使用相同hash地址的数据过多,那么这些数据所组成的hash链就更长,从而降低了查询效率!所以在选择系数的时候要选择尽量长(31 = 11111[2])的系数并且让乘法尽量不要溢出(如果选择大于11111的数,很容易溢出)的系数,因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。
31的乘法可以由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化,使用31的原因可能是为了更好的分配hash地址,并且31只占用5bits!
在java乘法中如果数字相乘过大会导致溢出的问题,从而导致数据的丢失.
而31则是素数(质数)而且不是很长的数字,最终它被选择为相乘的系数的原因不过与此!
我们再来开String中的hash计算:
h = 31 * h + val[i];
此句是将i位的值赋予一个算子,算子是由31的权重而来
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
相关资料:https://blog.csdn.net/myspacedemen/article/details/53353480
以下是toString的源码:
/**
* Returns a string representation of the object. In general, the
* <code>toString</code> method returns a string that
* "textually represents" this object. The result should
* be a concise but informative representation that is easy for a
* person to read.
* It is recommended that all subclasses override this method.
* <p>
* The <code>toString</code> method for class <code>Object</code>
* returns a string consisting of the name of the class of which the
* object is an instance, the at-sign character `<code>@</code>', and
* the unsigned hexadecimal representation of the hash code of the
* object. In other words, this method returns a string equal to the
* value of:
* <blockquote>
* <pre>
* getClass().getName() + '@' + Integer.toHexString(hashCode())
* </pre></blockquote>
*
* @return a string representation of the object.
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
Object类中有一个叫做toString的方法。该方法返回的是该Java对象的内存地址经过哈希算法得出的int类型的值在转换成十六进制。这个输出的结果可以等同的看作Java对象在堆中的内存地址。
package com.cal.toString;
public class Test1 {
public static void main(String[] args){
Object o1 = new Object();
System.out.println(o1.toString());
}
}
结果:java.lang.Object@7852e922
2.如果我们定义一个实体类,返回的结果又会是什么呢?
package com.cal.toString;
public class Test1 {
public static void main(String[] args){
Person p1 = new Person("king", 20);
System.out.println(p1.toString());
}
}
class Person{
String name;
int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
}
结果:com.cal.toString.Person@4e25154f很显然业务逻辑不太合适,不应该是一串看不懂的数字,而应该是符合逻辑的东西
3.这就表示Object中的toString方法已经不够用了。所以Object中的toString方法就是要被重写。
package com.cal.toString;
public class Test1 {
public static void main(String[] args){
Person p1 = new Person("king", 20);
System.out.println(p1.toString());
}
}
class Person{
String name;
int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return "Person[name="+name+", age="+age+"]";
}
}
这里就把toString方法重新写了,注意toString的返回值是String,结果明显就很符合逻辑了
结果:Person[name=king, age=20]