关于java中的hashcode和equals方法原理

1、介绍

java编程思想和很多资料都会对自定义javabean要求必须重写hashcode和equals方法,但并没有清晰给出为何重写此两个方法,至少不是非常的明确。

首先要确定的一件事是并不是“必须”,估计跟中英文语言习惯有关。hashcode方法只有在和hash类型的集合(比如HashMap和HashSet)配合使用时才会进行调用,否则是没有必要重写该方法的。

所以很多人会迷惑,自己并没有重写这方法,程序跑起来也没有问题。要说明这个问题必须要搞懂hashcode的真正作用以及使用场景。

2、hashcode

hashcode是java中Objet类定义的方法,默认返回的是对象的内存地址。但该方法的本意是散列。散列的话就必然涉及到在一定的空间中进行散列,所以hashcode方法一定是和集合配合使用的时候才用得到。

对象在空间散列化存储之后,其优势在于检索,如果散列算法处理得好,也就是能够保证对象在空间中尽可能均匀分布,则在检索时,一旦确定桶的方位(即下标值),就可以排除(n-1)/n的数据量,所有在大型数据集合中,hash之后的对象检索性能是非常高的。

java中的HashMap集合的内部实现是数组+链表实现,即Node[]数组方式实现的。而Node的源代码如下:

static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}

说明Node节点类是一种手拉手实现的链表方式,而Node[]在java编程思想中称为桶集合,数组中每个元素可以看成一个桶。HashMap集合在初始化时会先分配16个桶的空间,在进行put操作时,会先将KV封装成Node对象,再对key进行计算,判断应该划分到哪个桶中。

注意key在集合中是“不重复”的,因此如果key存在,就将新的value替换掉旧的value,如果key不存在,就在链表的末尾进行添加。如何判断key是否重复?HashMap类中的putVal方法源码如下:请注意注释地方的判断条件是

p.hash == hash
&&((k = p.key) == key || (key != null && key.equals(k))),

首先明确p是KV构成Node对象,该对象源码中可见含有四个属性,分别是int hash、K key、V value和Node<K,V> next,其中hash并不是key中的hashcode,是对key的hashcode计算之后生成的新hash值,我们称为新哈希,而生成新hash的算法是:

	int newhash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

翻译过来就是将旧哈希(我们称key对象的hashcode为就hash)向右移动16位之后和自身做异或运本意就是将高16位和低16位的值进行异或运算得到一个新的数值,这么做的意图非常明显,高位移位运算是想让更多的特征值参与进来,采用异或计算是想让数据更加分散。我们知道二进制位运算中有|、&、和~,很明显~是单元运算,如果采用|运算记录计算结果很大比例偏大,而采用&很大比例计算结果偏小,运算刚好高低比例相同。如此看来,设计也是非常巧妙的。



上面的判断条件翻译过来,就是:如果两个节点的新hash不同,则key一定不同。但如果新hash相同,还要判断key是否是同一对象,若是同一对象,则说明key相同,若不是同一对象,再判断equals方法是否相同。可以使用如下判断语句来描述获取更加清晰易懂:

if(newhash1 != newhash2){
//不同
}
else{
if(key1 == key2){
//相同
}
else{
if(key1.equals(key2)){
//相同
}
else{
//不同
}
}
}

桶的防止策略是对新hash对(桶数量-1)进行&运算产生的结果作为桶的下标值,由此可以看出,桶的数量一定是2的n次幂,默认是16只桶,即2^4次方,扩容以后会32,64以此类推下去。算法如下:

(n - 1) & hash		//(16 - 1) & hash与取摸操作是等效的。

3、equals方法

equals方法比较简单,就是判断对象内容是否相同,默认实现是判断内存地址。在java的集合中,List并不判断对象hashcode值,只判断equals方法。

4、总结

java集合中,HashMap和HashSet使用hashcode进行对象的散列存储,因此会用到hashcode方法,自然也会用到equals方法,List集合只使用equals方法判断对象是否相等。



在java的集合中,真正的集合只有数组和链表两种实现,HashMap是通过两者组合实现的,而HashSet内部是通过HashMap实现的,丢弃了HashMap中的value部分,使用了一个垃圾值(dummy)进行填充实现的。



所以究其根本,ArrayList和LinkedList应该是最基本的集合,数组列表内部封装数组,擅长读操作,大量写入时(尤其是在队首插入数据)性能较差,因为需要移动所有元素,而LinkdedList在写入时非常有优势,查询则较差,两者各有优缺点,在使用单机进行百万数据读写的评测中,数组列表读取能是列表是进10倍,而列表的写入能力是数组列表的10倍以上,差距还是非常明显的。

关于java中的hashcode和equals方法原理的更多相关文章

  1. K:java中的hashCode和equals方法

      hashCode和equals方法是Object类的相关方法,而所有的类都是直接或间接的继承于Object类而存在的,为此,所有的类中都存在着hashCode和equals.通过翻看Object类 ...

  2. java中的hashcode()和equals()

    equals()和hashcode()都继承自object类. equals() equals()方法在object类中定义如下: public boolean equals(Object obj) ...

  3. Java中的hashCode() 和 equals()的若干问题解答

    一.hashCode()的作用 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int ...

  4. Java中的“==操作符”和equals方法有什么区别

    Java中的"=="和equals方法究竟有什么区别? 1.==操作符 "=="操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的 ...

  5. Java 重写 hashCode() 和 equals() 方法

    1. hashCode 1.1 基本概念 hashCode 是 JDK 根据对象的地址算出来的一个 int 数字(对象的哈希码值),代表了该对象再内存中的存储位置. hashCode() 方法是超级类 ...

  6. 有关java中的hashCode问题

    1. HashSet集合存储数据的结构(哈希表) 1.1 什么是哈希表? 哈希表底层使用的也是数组机制,数组中也存放对象,而这些对象往数组中存放时的位置比较特殊,当需要把这些对象给数组中存放时,那么会 ...

  7. JAVA中的各种 哈希码(HashCode) 与 equals方法在HIBERNATE的实际应用[转载]

    1.什么是哈希码(HashCode) 在Java中,哈希码代表对象的特征.例如对象 Java代码 String str1 = “aa”, str1.hashCode= 3104 String str2 ...

  8. Java 中正确使用 hashCode 和 equals 方法

    在这篇文章中,我将告诉大家我对hashCode和equals方法的理解.我将讨论他们的默认实现,以及如何正确的重写他们.我也将使用Apache Commons提供的工具包做一个实现. 目录: hash ...

  9. (转)Java 中正确使用 hashCode 和 equals 方法

    背景:最近在编写持久化对象时候遇到重写equals和hashCode方法的情况,对这两个方法的重写做一个总结. 链接:https://www.oschina.net/question/82993_75 ...

随机推荐

  1. 【转】JavaScript里Function函数实现可变参数

    转载:  http://www.oschina.net/question/54100_15938 使用javascript类库函数时,经常会遇到一个函数,可以使用不同个数的参数的情况 比如:exp(v ...

  2. Lintcode 摊平嵌套的列表

    /** * // This is the interface that allows for creating nested lists. * // You should not implement ...

  3. 关于Django中路由层(URL-conf)

    关于路由层 路由层其实就是项目中urls.py那个文件,那里面存放着url和视图函数的对应关系它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来 ...

  4. mongodb在windows下安装

    下载地址:https://www.mongodb.com/download-center/community 我下载的是zip版本 新建目录:data |_db |_log cmd到所下载的mongo ...

  5. JS数组去重总结

    方法一: 双层循环,外层循环元素,内层循环时比较值 如果有相同的值则跳过,不相同则push进数组 Array.prototype.distinct = function(){ var arr = th ...

  6. formValidation校验

    引用: https://www.cnblogs.com/aliger/p/3898216.html

  7. MongoDB系列—— Window 搭建Mongodb 集群

    Mongodb的集群方式的搭建有三种:Replica Set / Sharding / Master-Slaver.这里只说明最简单的集群搭建方式(Replica Set) Replica Set M ...

  8. Silverlight TreeView 带 checkbox和图片

    前段时间做Silverlight TreeView 控件,但是要带checkbox和图片,在网上到处找相关的例子,效果图如下 xaml代码 <UserControl x:Class=" ...

  9. 吞吐率(Requests per second),缩写RPS

    计算公式:   吞吐率 = 总请求数 / 处理这些请求的总完成时间   Requests per second = Complete requests / Time taken for tests 吞 ...

  10. 30段有用的javascript代码

    1. 如何区分IE及非IE浏览器 if(!+[1,]){ console.log("这是IE浏览器"): } else{ console.log("这不是IE浏览器&qu ...