我们在程序中输入任意长度的二进制数据,通过hash算法就能产生对应的唯一的散列值。
hash算法的另一个作用是存储数据,假设我们要存储四个数据,通过hash算法,我们为每个数据产生一个不同的散列值,然后我们把散列值和数据存储的位置建立一种关联。因此,当你下次想要找到某个数据时,只需要知道hash值就知道了它存储的位置。
散列值我们可以理解为房子上的门牌号,我们想要找某个人,知道了门牌号就可以找到。
说明
说hashCode之前,先来看看Object类。
我们知道,Object类是java程序中所有类的直接或间接父类,处于类层次的最高点。在Object类里定义了很多我们常见的方法,包括我们要讲的hashCode方法,如下:
public final native Class> getClass();
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
根据这个方法的声明可知,该方法返回一个int类型的数值,也就是说你调用某个对象的hashCode()之后,会产生一个这个对象的hash码。
在java的很多类中都会重写equals和hashCode方法,如果两个对象用equals()方法进行比较,且结果相等,那么这两个对象调用hashCode()方法产生的hash码也是相等的。但是反过来说,equals()方法不相等的两个对象,hashCode()却是有可能相等的。
看下面一个例子:
class User{
String name;
int age;
public User(){}
public User(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object obj){
if(this == obj){
return true;
}
boolean b = obj instanceof User;
if(b){
User u = (User)obj;
if(this.age == u.age
&& this.name.equals(u.name)){
return true;
}
else{
return false;
}
}
else{
return false;
}
}
}
下面再写一个测试类:
import java.util.*;
import java.util.HashMap;
class Test{
public static void main(String[] args){
User u = new User("zhangsan",12);
HashMap map = new HashMap();
map.put(u,"abc");
String s = map.get(new User("zhangsan",12));
System.out.println(s);
}
}
按理说结果应该输出为“abc”,但是实际结果却是null。
map根据传入的键(u)来寻找对应的值,它通过计算这个对象的hashCode来判断在hashMap当中有没有存在这个值。而我们并没有复写hashCode()方法,因此User对象用的是默认的hashCode方法。默认的hashCode方法对于内存中不同的对象生成的hash码是不一样的。
那么,接下来我们就来重写hashCode()方法:
class User{
String name;
int age;
public User(){}
public User(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object obj){
if(this == obj){
return true;
}
boolean b = obj instanceof User;
if(b){
User u = (User)obj;
if(this.age == u.age
&& this.name.equals(u.name)){
return true;
}
else{
return false;
}
}
else{
return false;
}
}
public int hashCode(){
//返回一个固定值,所有对象调用hashCode方法生成的hash码都是一样的
return 12;
}
}
输出结果:
abc
虽然得到了想要的结果,但是上面程序中复写的方法是不合理的。
下面这段话摘自Effective Java一书:
在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。
如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。
对于第二条和第三条很好理解,但是第一条,很多时候就会忽略。在《Java编程思想》一书中的P495页也有同第一条类似的一段话:
“设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在讲一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码”。
hashCode()方法的重写和equals()方法的重写往往是一起的。因此我们可以这样重写hashCode()方法。
public int hashCode(){
//声明一个整型数据,值随便,一般是个质数
int result = 17;
//这里习惯用31这个奇素数
result = 31*result + age;
//name是String类型,String已经重写过hashCode(),这里生成它自己的hash码
result = 31*result + name.hashCode();
return result;
}
先看下面程序:
class Person
{
private String name;
public Person(String name)
{
this.name = name;
}
public void info()
{
System.out.println("此人名为:" + name);
}
}
public class PrintObject
{
public static void main(String[] args)
{
//创建一个Person对象,将之赋给给p变量
Person p = new Person("孙悟空");
//打印p所引用的Person对象
System.out.println(p);
}
}
上面程序创建了一个Person对象,然后使用System.out.println(p)方法输出Person对象。
输出结果:
Person@24ee5d13
这个奇怪的结果是怎么来的呢?System.out.println方法只能在控制台输出字符串,,当使用该方法输出Person对象的时候,实际上是输出的Person对象的toString()方法的返回值,也就是说下面两行效果一样:
System.out.println(p);
System.out.println(p.toString());
toString()方法是Object类里的一个实例方法,因此所有对象都具有toString()方法。
不仅如此,java对象都可以和字符串进行连接运算,当java对象都可以和字符串进行连接运算时,系统自动调用java对象toString方法的返回值和字符串进行连接运算,即下面两行效果一样:
String pStr = p + " ";
String pStr = p.toString() + " ";
toString()方法是一个非常特殊的方法,它是一个“自我描述”方法,该方法通常用于实现这样的功能:当程序员直接打印该对象时,系统将会输出该对象的“自我描述”信息,用以告诉外界该对象具有的状态信息。
Object类提供的toString()方法只是返回该对象实现类的类名+@+hashCode值,这个返回值并不能真正实现“自我描述“的功能,必须重写toString方法。
class Apple
{
private String color;
private double weight;
public Apple(){ }
//提供有参数的构造器
public Apple(String color , double weight)
{
this.color = color;
this.weight = weight;
}
public void setColor(String color)
{
this.color = color;
}
public String getColor()
{
return this.color;
}
public void setWeight(double weight)
{
this.weight = weight;
}
public double getWeight()
{
return this.weight;
}
public String toString()
{
return "一个苹果,颜色是:" + color + ",重量是:" + weight;
}
}
public class TestToString
{
public static void main(String[] args)
{
Apple a = new Apple("红色" , 5.68);
//打印apple对象
System.out.println(a);
}
}
输出结果:
一个苹果,颜色是:红色,重量是:5.68
从上面的结果可以看出,通过重写Apple类的toString方法,就可以让系统在打印Apple对象时打印出该对象的“自我描述”信息。
大部分时候,重写toString方法总是返回该对象所有令人感兴趣的信息所组成的字符串,通常可返回如下格式字符串:
类名[属性1 = 值1,属性2 = 值2,…]
因此可将上面Apple类的toString方法改写如下:
public String toString()
{
return "Apple[color=" + color + ",weight=" + weight + "]";
}
关于hashCode更多的介绍呢,可以查看以下链接进行学习:
http://www.cnblogs.com/dolphin0520/p/3681042.html
http://blog.csdn.net/jiangwei0910410003/article/details/22739953
http://www.tuicool.com/articles/fuE3Y3