Java

来源:互联网 发布:好牧人12网络牧养中心 编辑:程序博客网 时间:2024/05/17 20:28

equals和hashCode

HashCode的作用

Object的源码中,hashCode是这样定义的:

public native int hashCode();
JDK API中对HashCode的描述:
返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。
(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)


当我们向一个集合中添加某个元素,集合会首先调用hashCode方法,这样就可以直接定位它所存储的位置,若该处没有其他元素,则直接保存。
若该处已经有元素存在,就调用equals方法来匹配这两个元素是否相同,相同则不存,不同则散列到其他位置(可以参考HashMap)
这样处理,当我们存入大量元素时就可以大大减少调用equals()方法的次数,极大地提高了效率。

所以hashCode在上面扮演的角色为寻域(寻找某个对象在集合中区域位置)。
hashCode可以将集合分成若干个区域,每个对象都可以计算出他们的hash码,可以将hash码分组,每个分组对应着某个存储区域,
根据一个对象的hash码就可以确定该对象所存储区域,这样就大大减少查询匹配元素的数量,提高了查询效率。



HashCode与equals
两个对象的equals相等,这两个对象的hashCode必定相等!
两个对象的equals不相等,这两个对象的hashCode可能相等,可能不相等!



equlas()

超类Object中equals()方法,该方法主要用于比较两个对象是否相等,该方法的源码如下:


我们知道所有的对象都拥有标识(内存地址)和状态(数据),同时"=="是比较两个对象的内存地址,
所以说使用Object的equals()方法是比较两个对象的内存地址是否相同,
即:object1.equals(object2)为true,则表示object1和object2实际上是引用了同一个对象。

实际上JDK中,String,Math等封装类都对equals()方法进行了重写。

String的equals()方法:

public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = count;if (n == anotherString.count) {char v1[] = value;char v2[] = anotherString.value;int i = offset;int j = anotherString.offset;while (n-- != 0) {if (v1[i++] != v2[j++])return false;}return true;}}return false;}
对于这个代码段:if (v1[i++] != v2[j++])   return false; 可以看出String的eqauls()方法是进行内容比较,而不是引用比较,
至于其他的封装都差不多。



在equals()方法中使用getClass进行类型判断
我们复写equals()方法时,一般都是推荐使用getClass来进行类型判断,不推荐instanceof。

因为instanceof的作用是判断其左边对象是否为其右边对象的实例,返回boolean类型的数据。
可以用来判断继承中子类的实例是否为父类的实现。

注意这句话:"可以用来判 断继承中的子类的实例是否为父类的实现",正是这句话在作怪

我们来看一个栗子:

/*** 父类*/public class Person {public Person(String name) {this.name = name;}/** * 重写equals方法 */@Overridepublic boolean equals(Object obj) {if (obj instanceof Person) {Person person = (Person) obj;if (person.getName() == null || name == null) {return false;} else {return name.equalsIgnoreCase(person.getName());// 将此String与另一个String比较,不考虑大小写。}}return false;}protected String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}/*** 子类*/public class Employee extends Person {private int id;public Employee(String name, int id) {super(name);this.id = id;}/** * 重写equals方法 */@Overridepublic boolean equals(Object obj) {if (obj instanceof Employee) {Employee emp = (Employee) obj;return super.equals(obj) && emp.getId() == id;}return false;}public int getId() {return id;}public void setId(int id) {this.id = id;}}public class Test {/** * 上面父类Person和子类Employee都重写了equals()方法,不过Employee比父类多了一个id属性. *  * @param args */public static void main(String[] args) {Employee e1 = new Employee("chenssy", 23);Employee e2 = new Employee("chenssy", 25);Person p1 = new Person("chenssy");System.out.println(p1.equals(e1));System.out.println(p1.equals(e2));System.out.println(e1.equals(e2));}}输出:true,true,false

上面定义了两个员工和一个普通人,虽然他们同名,但是他们肯定不是同一人,所以按理说结果应该全部是false,和现在的结果对不上。

对于 "e1 != e2" 很好理解,因为他们不仅需要比较name还需要比较id,只是 p1即等于e1也等于e2,这是非常奇怪的,
因为e1 和e2明明是两个不同的类,为什么会出现这种情况呢?

首先p1.equals(e1),是调用p1的equals方法,该方法使用instanceof关键字来检查e1是否为Person类
然后看instanceof:判断其左边对象是否为其右边类的实例,也可以用来判断继承中子类的实例是否为父类的实现。
他们两者存在继承关系,所以结果为true,接着往下走,而两者name又相同,所以结果肯定是true。

所以出现上面的情况就是使用了关键字instanceof,很容易出现bug,所以在重写equals方法时推荐getClass进行类型判断,而不是instanceof。


参考资料:

http://www.cnblogs.com/chenssy/ 

《Thinking in Java》

原创粉丝点击