迅 网站 模板,维持一个素材网站要多少钱,如何查询网站以建设多长时间,友情链接交易平台java.lang.Object类中有两个非常重要的方法#xff1a; 1 2 public boolean equals(Object obj) public int hashCode()
Object类是类继承结构的基础#xff0c;所以是每一个类的父类。所有的对象#xff0c;包括数组#xff0c;都实现了在Object类中定义的方法。
回到…java.lang.Object类中有两个非常重要的方法 1 2 public boolean equals(Object obj) public int hashCode()
Object类是类继承结构的基础所以是每一个类的父类。所有的对象包括数组都实现了在Object类中定义的方法。
回到顶部
equals()方法详解
equals()方法是用来判断其他的对象是否和该对象相等. equals()方法在object类中定义如下
public boolean equals(Object obj) { return (this obj);
}
很明显是对两个对象的地址值进行的比较即比较引用是否相同。但是我们知道String 、Math、Integer、Double等这些封装类在使用equals()方法时已经覆盖了object类的equals()方法。 比如在String类中如下 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;
} 很明显这是进行的内容比较而已经不再是地址的比较。依次类推Math、Integer、Double等这些类都是重写了equals()方法的从而进行的是内容的比较。当然基本类型是进行值的比较。
它的性质有 自反性reflexive。对于任意不为null的引用值xx.equals(x)一定是true。 对称性symmetric。对于任意不为null的引用值x和y当且仅当x.equals(y)是true时y.equals(x)也是true。 传递性transitive。对于任意不为null的引用值x、y和z如果x.equals(y)是true同时y.equals(z)是true那么x.equals(z)一定是true。 一致性consistent。对于任意不为null的引用值x和y如果用于equals比较的对象信息没有被修改的话多次调用时x.equals(y)要么一致地返回true要么一致地返回false。 对于任意不为null的引用值xx.equals(null)返回false。
对于Object类来说equals()方法在对象上实现的是差别可能性最大的等价关系即对于任意非null的引用值x和y当且仅当x和y引用的是同一个对象该方法才会返回true。
需要注意的是当equals()方法被override时hashCode()也要被override。按照一般hashCode()方法的实现来说相等的对象它们的hash code一定相等。
回到顶部
hashcode() 方法详解
hashCode()方法给对象返回一个hash code值。这个方法被用于hash tables例如HashMap。
它的性质是 在一个Java应用的执行期间如果一个对象提供给equals做比较的信息没有被修改的话该对象多次调用hashCode()方法该方法必须始终如一返回同一个integer。 如果两个对象根据equals(Object)方法是相等的那么调用二者各自的hashCode()方法必须产生同一个integer结果。 并不要求根据equals(java.lang.Object)方法不相等的两个对象调用二者各自的hashCode()方法必须产生不同的integer结果。然而程序员应该意识到对于不同的对象产生不同的integer结果有可能会提高hash table的性能。
大量的实践表明由Object类定义的hashCode()方法对于不同的对象返回不同的integer。
在object类中hashCode定义如下
public native int hashCode(); 说明是一个本地方法它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法比如String、Integer、Double等这些类都是覆盖了hashcode()方法的。例如在String类中定义的hashcode()方法如下 public int hashCode() { int h hash; if (h 0) { int off offset; char val[] value; int len count; for (int i 0; i len; i) { h 31 * h val[off]; } hash h; } return h;
} 解释一下这个程序String的API中写到s[0]*31^(n-1) s[1]*31^(n-2) … s[n-1] 使用 int 算法这里 s[i] 是字符串的第 i 个字符n 是字符串的长度^ 表示求幂空字符串的哈希码为 0。 想要弄明白hashCode的作用必须要先知道Java中的集合。 总的来说Java中的集合Collection有两类一类是List再有一类是Set。前者集合内的元素是有序的元素可以重复后者元素无序但元素不可重复。这里就引出一个问题要想保证元素不重复可两个元素是否重复应该依据什么来判断呢 这就是Object.equals方法了。但是如果每增加一个元素就检查一次那么当元素很多时后添加到集合中的元素比较的次数就非常多了。也就是说如果集合中现在已经有1000个元素那么第1001个元素加入集合时它就要调用1000次equals方法。这显然会大大降低效率。 于是Java采用了哈希表的原理。哈希Hash实际上是个人名由于他提出一哈希算法的概念所以就以他的名字命名了。哈希算法也称为散列算法是将数据依特定算法直接指定到一个地址上初学者可以简单理解hashCode方法实际上返回的就是对象存储的物理地址实际可能并不是。 这样一来当集合要添加新的元素时先调用这个元素的hashCode方法就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素它就可以直接存储在这个位置上不用再进行任何比较了如果这个位置上已经有元素了就调用它的equals方法与新元素进行比较相同的话就不存了不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了几乎只需要一两次。 简而言之在集合查找时hashcode能大大降低对象比较次数提高查找效率
Java对象的eqauls方法和hashCode方法是这样规定的
1、相等相同的对象必须具有相等的哈希码或者散列码。
2、如果两个对象的hashCode相同它们并不一定相同。 以下是Object对象API关于equal方法和hashCode方法的说明
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.以上API说明是对之前2点的官方详细说明
关于第一点相等相同的对象必须具有相等的哈希码或者散列码为什么 想象一下假如两个Java对象A和BA和B相等eqauls结果为true但A和B的哈希码不同则A和B存入HashMap时的哈希码计算得到的HashMap内部数组位置索引可能不同那么A和B很有可能允许同时存入HashMap显然相等/相同的元素是不允许同时存入HashMapHashMap不允许存放重复元素。 关于第二点两个对象的hashCode相同它们并不一定相同 也就是说不同对象的hashCode可能相同假如两个Java对象A和BA和B不相等eqauls结果为false但A和B的哈希码相等将A和B都存入HashMap时会发生哈希冲突也就是A和B存放在HashMap内部数组的位置索引相同这时HashMap会在该位置建立一个链接表将A和B串起来放在该位置显然该情况不违反HashMap的使用原则是允许的。当然哈希冲突越少越好尽量采用好的哈希算法以避免哈希冲突。 所以Java对于eqauls方法和hashCode方法是这样规定的 1.如果两个对象相同那么它们的hashCode值一定要相同 2.如果两个对象的hashCode相同它们并不一定相同这里说的对象相同指的是用eqauls方法比较。 如不按要求去做了会发现相同的对象可以出现在Set集合中同时增加新元素的效率会大大下降。 3.equals()相等的两个对象hashcode()一定相等equals()不相等的两个对象却并不能证明他们的hashcode()不相等。 换句话说equals()方法不相等的两个对象hashcode()有可能相等我的理解是由于哈希码在生成的时候产生冲突造成的。反过来hashcode()不等一定能推出equals()也不等hashcode()相等equals()可能相等也可能不等。 在object类中hashcode()方法是本地方法返回的是对象的地址值而object类中的equals()方法比较的也是两个对象的地址值如果equals()相等说明两个对象地址值也相等当然hashcode()也就相等了在String类中equals()返回的是两个对象内容的比较当两个对象内容相等时Hashcode()方法根据String类的重写代码的分析也可知道hashcode()返回结果也会相等。以此类推可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的类在继承了object类的equals()和hashcode()方法后也会遵守这个原则。
回到顶部
Hashset、Hashmap、Hashtable与hashcode()和equals()的密切关系
Hashset是继承Set接口Set接口又实现Collection接口这是层次关系。那么Hashset、Hashmap、Hashtable中的存储操作是根据什么原理来存取对象的呢 下面以HashSet为例进行分析我们都知道在hashset中不允许出现重复对象元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢在java的集合中判断两个对象是否相等的规则是 1.判断两个对象的hashCode是否相等 如果不相等认为两个对象也不相等完毕 如果相等转入2 这一点只是为了提高存储效率而要求的其实理论上没有也可以但如果没有实际使用时效率会大大降低所以我们这里将其做为必需的。 2.判断两个对象用equals运算是否相等 如果不相等认为两个对象也不相等 如果相等认为两个对象相等equals()是判断两个对象是否相等的关键 为什么是两条准则难道用第一条不行吗不行因为前面已经说了hashcode()相等时equals()方法也可能不等所以必须用第2条准则进行限制才能保证加入的为非重复元素。
例1 1 package com.bijian.study;2 3 import java.util.HashSet;4 import java.util.Iterator;5 import java.util.Set;6 7 public class HashSetTest {8 9 public static void main(String args[]) {
10 String s1 new String(aaa);
11 String s2 new String(aaa);
12 System.out.println(s1 s2);
13 System.out.println(s1.equals(s2));
14 System.out.println(s1.hashCode());
15 System.out.println(s2.hashCode());
16 Set hashset new HashSet();
17 hashset.add(s1);
18 hashset.add(s2);
19 Iterator it hashset.iterator();
20 while (it.hasNext()) {
21 System.out.println(it.next());
22 }
23 }
24 } 运行结果
false
true
96321
96321
aaa 这是因为String类已经重写了equals()方法和hashcode()方法所以hashset认为它们是相等的对象进行了重复添加。
例2 1 package com.bijian.study;2 3 import java.util.HashSet;4 import java.util.Iterator;5 6 public class HashSetTest {7 8 public static void main(String[] args) {9 HashSet hs new HashSet();
10 hs.add(new Student(1, zhangsan));
11 hs.add(new Student(2, lisi));
12 hs.add(new Student(3, wangwu));
13 hs.add(new Student(1, zhangsan));
14
15 Iterator it hs.iterator();
16 while (it.hasNext()) {
17 System.out.println(it.next());
18 }
19 }
20 }
21
22 class Student {
23 int num;
24 String name;
25
26 Student(int num, String name) {
27 this.num num;
28 this.name name;
29 }
30
31 public String toString() {
32 return num : name;
33 }
34 } 运行结果
1:zhangsan
3:wangwu
2:lisi
1:zhangsan
为什么hashset添加了相等的元素呢这是不是和hashset的原则违背了呢回答是没有。因为在根据hashcode()对两次建立的new Student(1,“zhangsan”)对象进行比较时生成的是不同的哈希码值所以hashset把他当作不同的对象对待了当然此时的equals()方法返回的值也不等。 为什么会生成不同的哈希码值呢上面我们在比较s1和s2的时候不是生成了同样的哈希码吗原因就在于我们自己写的Student类并没有重新自己的hashcode()和equals()方法所以在比较时是继承的object类中的hashcode()方法而object类中的hashcode()方法是一个本地方法比较的是对象的地址引用地址使用new方法创建对象两次生成的当然是不同的对象了造成的结果就是两个对象的hashcode()返回的值不一样所以Hashset会把它们当作不同的对象对待。 怎么解决这个问题呢答案是在Student类中重新hashcode()和equals()方法。 class Student {int num;String name;Student(int num, String name) {this.num num;this.name name;}public int hashCode() {return num * name.hashCode();}public boolean equals(Object o) {Student s (Student) o;return num s.num name.equals(s.name);}public String toString() {return num : name;}
} 运行结果
1:zhangsan
3:wangwu
2:lisi
可以看到重复元素的问题已经消除根据重写的方法即便两次调用了new Student(1,zhangsan)我们在获得对象的哈希码时根据重写的方法hashcode()获得的哈希码肯定是一样的当然根据equals()方法我们也可判断是相同的所以在向hashset集合中添加时把它们当作重复元素看待了。
重写equals()和hashcode()小结 1.重点是equals重写hashCode只是技术要求为了提高效率 2.为什么要重写equals呢因为在java的集合框架中是通过equals来判断两个对象是否相等的 3.在hibernate中经常使用set集合来保存相关对象而set集合是不允许重复的。在向HashSet集合中添加元素时其实只要重写equals()这一条也可以。但当hashset中元素比较多时或者是重写的equals()方法比较复杂时我们只用equals()方法进行比较判断效率也会非常低所以引入了hashCode()这个方法只是为了提高效率且这是非常有必要的。比如可以这样写
public int hashCode(){ return 1; //等价于hashcode无效
}