深入理解容器系列片之五--------hashcode和equal

1、在jdk源码中,hashcode和equal是怎么定义的

public native int hashCode();
public boolean equals(Object obj) {
	return (this == obj);
    }
由上面可知:

hashCode()调用的是一个native方法,这个方法具体做了什么,不知道。而equal方法是用==来比较两个对象的引用,也就是说两个对象的地址,只有这两个对象的地址相等的时候,这两个对象才相等。


2、hashcode和equal有什么用?

说白了,hashcode就是给对象提供一个hash值,hash值是用来在散列表里面用的,所以hashcode就是在用到散列表的时候用。

而equal就是用来比较两个对象是否相等。有人说:我们不是有==这个符号了吗?为什么还要这个equal呢?

好吧我们就来分析它们之间的区别吧:

在jdk源码中,如上所示,equal函数与==意义是一样的,很显然,在这里equal和==是等价的。从这里找不到我们需要的答案。既然书上找不到,现在我们又是研究OO编程,那我们就到生活中去找找。以下的见解纯属个人的,如有错误,望指出。

有一天卖切糕的跟卖米碰到了一起:卖切糕的叫:==;卖米交:equal。下面是他们的对话。

equal:你这一块切糕有多重?

==:     他指了指切糕上面的标签,10公斤。

equal:他看了看自己的米,大约也是10公斤左右,他就对卖切糕的说,我们来做一个交易怎么样?

==:什么交易?

equal:我这里有10公斤米,你那里有10公斤切糕,我们的数量相等,来做一个公平交换吧,你拿我这10公斤米,我就要你那10公斤切糕,怎么样?

==:卖切糕那人瞪着那卖米的人说:我这里是10公斤切糕,而你那里是10公斤米,你凭什么跟我换?

equal:买米的理直气壮的说:你那里是10公斤,我这里也是10公斤,从数量上来说就是相等的。所以可以换。

==:卖切糕一听,就很不爽,操刀就上,边赶边大吼道:我这是切糕,你那是米,从本质上来说:他们就是不想等的。

.......故事到这里结束了。

这里有两个重点词,一个是从数量上来说,一个是从本质上来说。那么在计算机的世界里面,什么是“本质呢”,什么又是“数量”呢?以下是相关介绍

在java的世界里面,类型可以分为三类(不是两类吗?怎么会有三类呢)详情请点击这里

第一类:基本类型;

第二类:引用类型;

第三类:null类型(这个很特殊)。

第一类比较大小是怎么比较的呢?

用==,而不用equal()。为什么不能用equal(),因为基本类型用不了。几种基本类型的比较请点击这里

第二类比较大小一般都是用equal,为什么不用==,详情请点击这里

第三类比较大小一般无意义,详情点击这里


3、我们为什么要重写hashcode和equal?

重写equal的理由:

从开始就知道,父类Object的equal函数就是返回了this == obj的值,从第二点我们可以知道this == obj其实是这两个对象地址之间的比较,而不是内容之间的比较,而从逻辑上面来说,两个对象在内存中的地址与我们比较两个对象是否相等没有丝毫联系,因为对象存在内存的什么地方不是我们关心的问题,对象里面的内容才是我们关心的要点,所以我们要比较对象里面的内容,而不是比较两个对象在内存中的地址。基于这个理由,我们就是不得不重写equal函数。当然不是所有的情况都是要重写equal的,对于不用进行对象之间比较操作都不用重写equal,但是一般情况我们都是会进行对象之间的比较,所以最好是要重写。

重写hashcode:

从jdk的源码可以知道public native int hashCode();她是一个本地方法,在这个地方我就认为它返回的是那个对象的地址的整数值。因为hashCode是用在散列表的时候用的,object的hashcode返回的是一个整数,而且是一个不错的整数,因为对象不同就会返回不同的整数,这样散列对象很好。似乎我们就不用重写hashcode,因为这样很好的,但是我们仔细想一想,这样真的行吗。接下来举一个例子:

public class Student {
private int id;
private String name;
private String aa;
public Student(int id,String name){
	this.id=id;


	this.name=name;
}
public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (obj == null)
		return false;
	if (getClass() != obj.getClass())
		return false;
	Student other = (Student) obj;
	if (id != other.id)
		return false;
	if (name == null) {
		if (other.name != null)
			return false;
	} else if (!name.equals(other.name))
		return false;
	return true;
}
}
public class StudentHelp {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Map<Student,String> map=new HashMap<Student,String>();
		map.put(new Student(1,"aa"), "aa");
		map.put(new Student(1,"bb"), "bb");
		map.put(new Student(1,"aa"), "cc");
		System.out.println(map.get(new Student(1,"aa")));
	}

}
分析上面的代码:
第一种:如果没有重写hashCode(),那么结果就是可能是下面这张图:
深入理解容器系列片之五--------hashcode和equal
(图一)
如果重写hashcode,怎么重写下面说两种情况:
第二种:(恒定的值)
public int hashCode(){
return 1;
}


运行上面的程序,结果如下图:
深入理解容器系列片之五--------hashcode和equal
第三种:(不同对象产生变化个值)
eclipse自动产生的
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
运行程序,结果如下图:
深入理解容器系列片之五--------hashcode和equal
如果对上诉程序,运行的结果有迷惑,请点击这里

4、我们怎么重写hashcode和equal函数?
怎么重写equal
重写equal就看我们从什么方面来比较两个对象了,这个可以选择类里面的一个或者多个成员变量来进行比较
怎么重写hashcoe
在equal函数里面用到的比较量,都要在hashcode里面用到,这样才能遵守,相同的对象才会产生相同hashcode。
关键点:我们不需要重写equal和hashcode,eclipse可以自动产生,我在这里说这么多主要是想把原理弄懂。
5、我们该遵守的规则?为什么要遵守这些规则?
关于重写hashcode和equal函数,我们要遵守的一般规则是什么?
重写了equal,最好是要重写hashcode
解析:反面论证这天定理,如果我们重写了equal,但是没有重写hashcode就会出现上面的图一情况,相同的对象会hash不到同一个槽里面去
重写了hashcode,一定也要重写equal吗?
解析:不一定,因为我们没必要保证产生相同的hash值的对象,就是相同的对象。



















深入理解容器系列片之五--------hashcode和equal

上一篇:寒假学习 第四、五天 (linux 高级编程)


下一篇:H3C DHCP中继实战