hashCode()与equals()的关联
2017-08-17 20:10
106 查看
一、Hash表数据结构介绍
请参考 哈希表详解二、equals的内部实现
equals()的定义位于Object.class中:public boolean equals(Object obj) { return (this == obj); }
从这里可以看出,如果不重写的话,equals默认就是断定两个对象的内存地址是否相同。如果内存地址相同,必然是同一个对象;如果内存地址不相同,必然不是同一个对象。
三、hashCode()介绍
(一)hashCode()内部实现
hashCode()的定义位于Object.class中:public native int hashCode();
根据这个方法的声明可知,该方法返回一个int类型的数值,并且是本地方法,因此在Object类中并没有给出具体的实现。
小贴士:
1 什么是native方法
简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C或C++。
这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern “C”告知C++编译器去调用一个C的函数。
2 为什么要用native方法
(1)与java环境外交互:
有时java应用需要与java外面的环境交互。这是本地方法存在的主要原因,你可以想想java需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解java应用之外的繁琐的细节。
(2)与操作系统交互:
JVM支持着java语言本身和运行时库,它是java程序赖以生存的平台,它由一个解释器(解释字节码)和一些连接到本地代码的库组成。然而不管怎 样,它毕竟不是一个完整的系统,它经常依赖于一些底层(underneath在下面的)系统的支持。这些底层系统常常是强大的操作系统。通过使用本地方法,我们得以用java实现了jre的与底层系统的交互,甚至JVM的一些部分就是用C写的,还有,如果我们要使用一些java语言本身没有提供封装的操作系统的特性时,我们也需要使用本地方法。
(二)为何使用hashCode()?
对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode。在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。为什么这么说呢?考虑一种情况,当向基于散列的集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在)
也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。下面这段代码是java.util.HashMap的中put方法的具体实现:
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
put方法是用来向HashMap中添加新的元素,从put方法的具体实现可知,会先调用hashCode方法得到该元素的hashCode值,然后查看table中是否存在该hashCode值,如果存在则调用equals方法重新确定是否存在该元素,如果存在,则更新value值,否则将新的元素添加到HashMap中。从这里可以看出,hashCode方法的存在是为了减少equals方法的调用次数,从而提高程序效率。
有些朋友误以为默认情况下,hashCode返回的就是对象的存储地址,事实上这种看法是不全面的,确实有些JVM在实现时是直接返回对象的存储地址,但是大多时候并不是这样,只能说可能存储地址有一定关联。
四、两者的关系
(一)如果equals方法得到的结果为true,则两个对象的hashcode是否相等?
例1:重写equals()但不重写hashCode()Student.java
public class Student { private int age; private String name; public Student() { } public Student(int age, String name) { super(); this.age = age; this.name = name; } public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } @Override 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 (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Test.java
import java.util.HashSet; import java.util.LinkedList; import java.util.Set; public class Test { public static void main(String[] args) { LinkedList<Student> list = new LinkedList<Student>(); Set<Student> set = new HashSet<Student>(); Student stu1 = new Student(3,"Li Si"); Student stu2 = new Student(3,"Li Si"); System.out.println("stu1 == stu2 : "+(stu1 == stu2)); System.out.println("hashcode of stu1: " + stu1.hashCode()); System.out.println("hashcode of stu2: " + stu2.hashCode()); System.out.print("stu1.hashCode()==stu2.hashCode(): "); System.out.println(stu1.hashCode()==stu2.hashCode()); System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2)); list.add(stu1); list.add(stu2); System.out.println("list size:"+ list.size()); set.add(stu1); set.add(stu2); System.out.println("set size:"+ set.size()); } }
运行结果:
stu1 == stu2 : false hashcode of stu1: 1291472364 hashcode of stu2: 1158801519 stu1.hashCode()==stu2.hashCode(): false stu1.equals(stu2) : true list size:2 set size:2
例2:重写equals()和hashCode()
Student.java
public class Student { private int age; private String name; public Student() { } public Student(int age, String name) { super(); this.age = age; this.name = name; } public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override 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 (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Test.java的代码与例1中的代码一致,在此不再罗列。
运行结果:
stu1 == stu2 : false hashcode of stu1: 73350135 hashcode of stu2: 73350135 stu1.hashCode()==stu2.hashCode(): true stu1.equals(stu2) : true list size:2 set size:1
例3:重写equals()和hashCode()
Student.java
public class Student { private int age; private String name; private static int index = 5; public Student() { } public Student(int age, String name) { super(); this.age = age; this.name = name; } public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age + index; index++; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override 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 (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Test.java的代码与例1中的代码一致,在此不再罗列。
运行结果:
stu1 == stu2 : false hashcode of stu1: 73350290 hashcode of stu2: 73350321 stu1.hashCode()==stu2.hashCode(): false stu1.equals(stu2) : true list size:2 set size:2
结论:从上面三个例子可以看出,如果equals方法得到的结果为true,则两个对象的hashcode可能相等,也可能不相等。
(二)如果equals方法得到的结果为false,则两个对象的hashcode值是否不相等?
例4:不重写equals()和hashCode()Student.java
public class Student { private int age; private String name; public Student() { } public Student(int age, String name) { super(); this.age = age; this.name = name; } public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } }
Test.java
import java.util.HashSet; import java.util.LinkedList; import java.util.Set; public class Test { public static void main(String[] args) { LinkedList<Student> list = new LinkedList<Student>(); Set<Student> set = new HashSet<Student>(); Student stu1 = new Student(3,"Zhang San"); Student stu2 = new Student(4,"Li Si"); System.out.println("stu1 == stu2 : "+(stu1 == stu2)); System.out.println("hashcode of stu1: " + stu1.hashCode()); System.out.println("hashcode of stu2: " + stu2.hashCode()); System.out.print("stu1.hashCode()==stu2.hashCode(): "); System.out.println(stu1.hashCode()==stu2.hashCode()); System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2)); list.add(stu1); list.add(stu2); System.out.println("list size:"+ list.size()); set.add(stu1); set.add(stu2); System.out.println("set size:"+ set.size()); } }
运行结果:
stu1 == stu2 : false hashcode of stu1: 595755354 hashcode of stu2: 1291472364 stu1.hashCode()==stu2.hashCode(): false stu1.equals(stu2) : false list size:2 set size:2
例5:重写hashCode()
Test.java
public class Student { private int age; private String name; public Student() { } public Student(int age, String name) { super(); this.age = age; this.name = name; } public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } @Override public int hashCode() { return 1; } }
Test.java与例4中的Test.java代码一致,在此不再罗列。
运行结果:
stu1 == stu2 : false hashcode of stu1: 1 hashcode of stu2: 1 stu1.hashCode()==stu2.hashCode(): true stu1.equals(stu2) : false list size:2 set size:2
结论:由例4和例5可以看出,如果equals方法得到的结果为false,则两个对象的hashcode值可能相等,也可能不相等。
(三)如果两个对象的hashcode值相等,则equals方法得到的结果未是否为true?
结论:由例2和例5可以看出,如果两个对象的hashcode值相等,则equals方法得到的结果可能为true,也可能为false。(四)如果两个对象的hashcode值不相等,则equals方法得到的结果是否为false?
结论:由例3和例4可以看出,如果两个对象的hashcode值不相等,则equals方法得到的结果可能为true,也可能为false。相关文章推荐
- hibernate 一对多双向关联在多方重写equals和hashCode方法
- JAVA中Object的equals和hashCode的关联
- HashSet TreeSet 和 equals、hashCode、comparable接口之间关联
- 【Java】 ==、equals()和hashCode()的关联
- Java重写equals与hashCode的注意事项 | Java基础
- Java 中 hashCode 和 equals 方法 小结
- HashCode 和 Equals 的使用 - 使用自定义对象作为HashMap的Key例子
- java的hashCode和equals函数在HashMap容器中的作用
- 关于Object的equals()及hashCode()
- java中的hashcode和equals的区别?
- java里equals和hashCode的关系
- 关于重写equals,hashcode以及compareTo方法!
- 对象重写equals时为什么也要重写hashCode方法
- equals和 hashcode比较
- 浅谈equals和hashcode
- java中equals与hashCode还有tostring方法学习记录
- Java实战equals()与hashCode()
- java 中 equals() 相等的两个对象,hashcode() 一定相等
- equals 和 ==, equals 和 hashcode
- java 重写 hashCode、equals