关于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. java的MAP接口方法

    常用操作说明 void clear() 从此映射中移除所有映射关系(可选操作). boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true. b ...

  2. 安装OFFICE2016错误代码0-1018(0)的解决方案 和激活方案

    一.安装OFFICE2016错误代码0-1018(0)的解决方案 下面方法亲测有效:Step 1. Go to C:\Program Files\Common Files\microsoft shar ...

  3. Kibana6.2.2源码入口

    后端入口 \kibana-6.2.2\src\server\kbn_server.js 前端入口 kibana-6.2.2\src\ui\public\chrome\chrome.js 页面框架模板 ...

  4. 找到一篇关于 Oracle 全文检索实践 的文章

    http://www.iteye.com/topic/1118055 有详细的例子记录了Oracle 全文检索的使用.

  5. 微软原版SQL Helper

    代码 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-- ...

  6. Photoshop入门教程(四):混合模式

    学习心得:混合模式在Photoshop常容易被忽视,最大原因就是它所处的位置比较隐蔽,在图层面板左上部的角落里.使用混合模式,决定图像中上图层像素如何与图像中的下层像素进行混合,使图层的叠加更加炫酷. ...

  7. google chrome浏览器自动填充解决方案

    在chrome浏览器中,浏览器对于[1]type为password和text的.[2]带有name或者id属性的<input>标签会有自动填充表单功能,虽然会给用户记住密码带来一定的便利, ...

  8. Java日志组件2---Log4j(org.apache.log4j.Logger)

    如果我们在项目中,需要记录的东西并不多,而且也不需要有太多区分,使用jdk的自带Log完全可以解决问题.但是,在开发的过程中,大多数项目都比较大,为方便找到程序的bug,都是需要系统的记录日志的.这里 ...

  9. gcc 4.9编译

    参考 http://blog.csdn.net/hzhxxx/article/details/28634893

  10. entity framework discriminator error

    前几天使用code first碰到错误:列名 'Discriminator' 无效.这是一个很少见的错误,搜索了很久才发现是code first的poco实体对象的继承问题. 比如,我定义了一个实体类 ...