《Effective Java》中推荐的hashCode算法
来源:互联网 发布:网络直播的现状 编辑:程序博客网 时间:2024/05/22 08:15
http://blog.csdn.net/error_case/article/details/46503103
Google首席Java架构师Joshua Bloch在他的著作《Effective Java》中提出了一种简单通用的hashCode算法:
1. 初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result =17;
2. 选取equals方法中用于比较的所有域,然后针对每个域的属性进行计算:
(1) 如果是boolean值,则计算f ? 1:0
(2) 如果是byte\char\short\int,则计算(int)f
(3) 如果是long值,则计算(int)(f ^ (f >>> 32))
(4) 如果是float值,则计算Float.floatToIntBits(f)
(5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int
(6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0
(7) 如果是数组,没必要自己去重新遍历一遍数组,java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上,
java.util.Arrays.hashCode(long[])的具体实现:
- public static int hashCode(long a[]) {
- if (a == null)
- return 0;
- int result = 1;
- for (long element : a) {
- int elementHash = (int)(element ^ (element >>> 32));
- result = 31 * result + elementHash;
- }
- return result;
- }
Arrays.hashCode(...)只会计算一维数组元素的hashCOde,如果是多维数组,那么需要递归进行hashCode的计算,那么就需要使用Arrays.deepHashCode(Object[])方法。
4. 测试,hashCode方法是否符合文章开头说的基本原则,这些基本原则虽然不能保证性能,但是可以保证不出错。
这个算法存在这么几个问题需要探讨:
1. 为什么初始值要使用非0的整数?这个的目的主要是为了减少hash冲突,考虑这么个场景,如果初始值为0,并且计算hash值的前几个域hash值计算都为0,那么这几个域就会被忽略掉,但是初始值不为0,这些域就不会被忽略掉,示例代码:
- import java.io.Serializable;
- public class Test implements Serializable {
- private static final long serialVersionUID = 1L;
- private final int[] array;
- public Test(int... a) {
- array = a;
- }
- @Override
- public int hashCode() {
- int result = 0; //注意,此处初始值为0
- for (int element : array) {
- result = 31 * result + element;
- }
- return result;
- }
- public static void main(String[] args) {
- Test t = new Test(0, 0, 0, 0);
- Test t2 = new Test(0, 0, 0);
- System.out.println(t.hashCode());
- System.out.println(t2.hashCode());
- }
- }
如果hashCode中result的初始值为0,那么对象t和对象t2的hashCode值都会为0,尽管这两个对象不同。但如果result的值为17,那么计算hashCode的时候就不会忽略这些为0的值,最后的结果t1是15699857,t2是506447
2.为什么每次需要使用乘法去操作result? 主要是为了使散列值依赖于域的顺序,还是上面的那个例子,Test t = new Test(1, 0)跟Test t2 = new Test(0, 1), t和t2的最终hashCode返回值是不一样的。
3. 为什么是31?31是个神奇的数字,因为任何数n * 31就可以被JVM优化为 (n << 5) -n,移位和减法的操作效率要比乘法的操作效率高的多。
例子:
import java.util.Arrays;import java.util.List;import java.util.Map;public class HashTest { int intVar; long longVar; boolean booleanVar; float floatVar; double doubleVar; byte byteVar; String stringVar; Object objectVar; A aVar; List<A> listVar; Map<String, A> mapVar; long[] longArrayVar; A[] aArrayVar; HashTest hashTestVar; HashTest[] hashTestArrayVar; @Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + ((aVar == null) ? 0 : aVar.hashCode());result = prime * result + (booleanVar ? 1231 : 1237);result = prime * result + byteVar;long temp;temp = Double.doubleToLongBits(doubleVar);result = prime * result + (int) (temp ^ (temp >>> 32));result = prime * result + Float.floatToIntBits(floatVar);result = prime * result + Arrays.hashCode(hashTestArrayVar);result = prime * result+ ((hashTestVar == null) ? 0 : hashTestVar.hashCode());result = prime * result + intVar;result = prime * result + ((listVar == null) ? 0 : listVar.hashCode());result = prime * result + Arrays.hashCode(longArrayVar);result = prime * result + (int) (longVar ^ (longVar >>> 32));result = prime * result + ((mapVar == null) ? 0 : mapVar.hashCode());result = prime * result+ ((objectVar == null) ? 0 : objectVar.hashCode());result = prime * result + Arrays.hashCode(aArrayVar);result = prime * result+ ((stringVar == null) ? 0 : stringVar.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;HashTest other = (HashTest) obj;if (aVar == null) {if (other.aVar != null)return false;} else if (!aVar.equals(other.aVar))return false;if (booleanVar != other.booleanVar)return false;if (byteVar != other.byteVar)return false;if (Double.doubleToLongBits(doubleVar) != Double.doubleToLongBits(other.doubleVar))return false;if (Float.floatToIntBits(floatVar) != Float.floatToIntBits(other.floatVar))return false;if (!Arrays.equals(hashTestArrayVar, other.hashTestArrayVar))return false;if (hashTestVar == null) {if (other.hashTestVar != null)return false;} else if (!hashTestVar.equals(other.hashTestVar))return false;if (intVar != other.intVar)return false;if (listVar == null) {if (other.listVar != null)return false;} else if (!listVar.equals(other.listVar))return false;if (!Arrays.equals(longArrayVar, other.longArrayVar))return false;if (longVar != other.longVar)return false;if (mapVar == null) {if (other.mapVar != null)return false;} else if (!mapVar.equals(other.mapVar))return false;if (objectVar == null) {if (other.objectVar != null)return false;} else if (!objectVar.equals(other.objectVar))return false;if (!Arrays.equals(aArrayVar, other.aArrayVar))return false;if (stringVar == null) {if (other.stringVar != null)return false;} else if (!stringVar.equals(other.stringVar))return false;return true;} }class A{int a;long b;String c;@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + a;result = prime * result + (int) (b ^ (b >>> 32));result = prime * result + ((c == null) ? 0 : c.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;A other = (A) obj;if (a != other.a)return false;if (b != other.b)return false;if (c == null) {if (other.c != null)return false;} else if (!c.equals(other.c))return false;return true;}}
0 0
- 《Effective Java》中推荐的hashCode算法
- 《Effective Java》中推荐的hashCode算法
- JS中实现JAVA的hashCode算法
- Effective Java之接近理想的hashCode
- Java equals() ,hashCode(),toString() 的推荐写法。
- java中HashCode的使用
- java 中hashcode的理解
- Java中hashcode的理解
- Java中hashcode的理解
- Java中hashCode的作用
- Java中hashcode的理解
- Java中hashCode的作用
- java中hashcode()的作用
- Java中hashCode()的作用
- java中HashCode的作用
- Java中hashCode的作用
- java中hashcode的计算方法
- Java中hashCode的作用
- Android横竖屏总结
- css定位
- java中为什么需要接口
- 日报2015/10/28(极客学院安卓视频学习)
- Unity3D热更新全书-何谓热更新,为何热更新,如何热更新
- 《Effective Java》中推荐的hashCode算法
- 有关于变量类型的隐含问题
- 关于启动页面
- 欢迎使用CSDN-markdown编辑器
- 如何成为更高级别的iOS开发工程师?
- linux---sz命令
- CSS3弹性布局的使用
- php exec传参数问题
- Java Swing模拟按键事件