java重写equals和hashCode方法
一.重写equals方法
如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。
利用equals比较八大包装对象(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址.
重写规则
(1)自反性: 对于任意的引用值x,x.equals(x)一定为true.
(2)对称性: 对于任意的引用值x和y,当x.equals(y)返回true,y.equals(x)也一定返回true
(3)传递性: 对于任意的引用值x,y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也一定返回true
(4)一致性: 对于任意的引用值x和y,如果用于equals比较的对象信息没有被修改,多次调用x.equals(y)要么一致地返回true,要么一致地返回false
(5)非空性: 对于任意的非空引用值x,x.equals(null)一定返回false
重写equals方法修饰符必须是public,因为是重写的Object的方法; 参数类型必须是Object.
重写equals方法后必须重写hashCode方法,否则两个等价对象可能得到不同的hashCode,这在集合框架中使用可能产生严重后果
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
二.重写hashCode方法
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashCode值来进行判断是否相同的。
通用的hashCode算法实现
1.首先初始化一个整形变量,为此变量赋予一个非零的常数值,比如
int result = 17;
初始值要使用非0的整数,以减少hash冲突.假如所有的域引用都为null,按照下面的算法,采用0作为初始值则计算出来的结果都为0.
2.选取equals方法中用于比较的所有域,然后针对每个域的属性进行计算:
(1) 如果是boolean类型,则计算根据是否为真进行处理,如: f ? 1231 : 1237
(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) 如果是数组,那么需要为每个元素当做单独的域来处理.
如果你使用的是1.5及以上版本的JDK,那么没必要自己去重新遍历一遍数组,java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上.
ps:上面所提到的基本元素类型的hashCode计算,其算法同JDK中其包装器类型所覆盖的hashCode逻辑一致,具体如下
java.lang.Long的hashCode实现:
public int hashCode() {
return (int)(value ^ (value >>> 32));
}
java.lang.Float的hashCode实现:
public int hashCode() {
return floatToIntBits(value);
}
java.lang.double的hashCode实现:
public int hashCode() {
long bits = doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}
java.lang.boolean的hashCode实现
public int hashCode() {
return value ? 1231 : 1237;
}
其它类型,可按如下实现
java.lang.byte的hashCode实现,char\short\int同理也可按此实现
public int hashCode() {
return value;
}
3.将上述hashCode值与质数因子进算得到新的值
result = result * 31 + hashCode();
4.此算式的几点解释:
使用乘法去操作result: 目的是为了使散列值依赖于域的顺序.如同时包含了两个相同类型的域,以保证顺序不同产生不同的hashCode.
使用31作为质数因子: 31是个神奇的数字,因为任何数n * 31就可以被JVM优化为(n << 5) - n, 移位和减法的操作效率要比乘法的操作效率高的多.
三.一个具体的实现类的例子
class Item {
private String mName;
private long mId;
private long mValue; public Item(String name, long id) {
mName = name;
mId = id;
} public Item(String name, long id, long value) {
mName = name;
mId = id;
mValue = value;
} @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Item))
return false;
Item object = (Item) obj; if (mName == null) {
if (object.mName != null)
return false;
} else if (!mName.equals(object.mName)) {
return false;
} if (mId != object.mId)
return false; if (mValue != object.mValue)
return false; return true;
} @Override
public int hashCode() {
int result = 17;
result = 31 * result + (mName != null ? mName.hashCode() : 0);
result = 31 * result + (int) (mId ^ (mId >>> 32));
result = 31 * result + (int) (mValue ^ (mValue >>> 32));
return result;
// HashCodeBuilder builder = new HashCodeBuilder();
// builder.append(mId).append(mName).append(mValue);
// return builder.getHashCode();
} @Override
public String toString() {
return "Item[mName=" + mName + ", mId=" + mId + ", mValue=" + mValue
+ "]";
}
}
四.一个hashCode生成器类
/**
* hashCode生成器
*/
public class HashCodeBuilder { private final int mMultiplierFactor;
private int mHashCode; public HashCodeBuilder() {
this(17, 31);
} public HashCodeBuilder(int initialCode) {
this(initialCode, 31);
} public HashCodeBuilder(int initialCode, int multiplierFactor) {
mHashCode = initialCode;
mMultiplierFactor = multiplierFactor;
} public int getHashCode() {
return mHashCode;
} public HashCodeBuilder append(int[] array) {
for (int element : array) {
append(element);
}
return this;
} public HashCodeBuilder append(byte[] array) {
for (byte element : array) {
append(element);
}
return this;
} public HashCodeBuilder append(short[] array) {
for (short element : array) {
append(element);
}
return this;
} public HashCodeBuilder append(char[] array) {
for (char element : array) {
append(element);
}
return this;
} public HashCodeBuilder append(long[] array) {
for (long element : array) {
append(element);
}
return this;
} public HashCodeBuilder append(float[] array) {
for (float element : array) {
append(element);
}
return this;
} public HashCodeBuilder append(double[] array) {
for (double element : array) {
append(element);
}
return this;
} public HashCodeBuilder append(boolean[] array) {
for (boolean element : array) {
append(element);
}
return this;
} public HashCodeBuilder append(int element) {
mHashCode = mHashCode * mMultiplierFactor + element;
return this;
} public HashCodeBuilder append(byte element) {
mHashCode = mHashCode * mMultiplierFactor + element;
return this;
} public HashCodeBuilder append(short element) {
mHashCode = mHashCode * mMultiplierFactor + element;
return this;
} public HashCodeBuilder append(char element) {
mHashCode = mHashCode * mMultiplierFactor + element;
return this;
} public HashCodeBuilder append(long element) {
mHashCode = mHashCode * mMultiplierFactor
+ ((int) (element ^ (element >>> 32)));
return this;
} public HashCodeBuilder append(float element) {
return append(Float.floatToIntBits(element));
} public HashCodeBuilder append(double element) {
return append(Double.doubleToLongBits(element));
} public HashCodeBuilder append(boolean element) {
mHashCode = mHashCode * mMultiplierFactor + (element ? 1231 : 1237);
return this;
} /**
* 处理自定义类型元素或数组
* @param object,数组或一个元素
* @return
*/
public HashCodeBuilder append(Object object) {
if (object == null)
return appendNull(); Class<?> klassType = object.getClass().getComponentType();
// klass为null,表示一个元素,否则表示一个数组
if (klassType == null) {
return appendElement(object);
}
return deepHashCode((Object[])object);
} private HashCodeBuilder deepHashCode(Object[] array) {
if (array == null) {
return appendNull();
}
for (Object element : array) {
deepHashCodeElement(element);
}
return this;
} private HashCodeBuilder deepHashCodeElement(Object element) {
if (element == null) {
return appendNull();
} Class<?> klass = element.getClass().getComponentType(); // klass为null,表示一个元素,否则表示一个数组
if (klass == null) {
return appendElement(element);
} // isPrimitive返回true则为基本类型,如 int\char\...等
if (!klass.isPrimitive()) {
return deepHashCode((Object[]) element);
} if (klass.equals(int.class)) {
return append((int[]) element);
}
if (klass.equals(char.class)) {
return append((char[]) element);
}
if (klass.equals(boolean.class)) {
return append((boolean[]) element);
}
if (klass.equals(byte.class)) {
return append((byte[]) element);
}
if (klass.equals(long.class)) {
return append((long[]) element);
}
if (klass.equals(float.class)) {
return append((float[]) element);
}
if (klass.equals(double.class)) {
return append((double[]) element);
}
return append((short[]) element);
} private HashCodeBuilder appendElement(Object element) {
if (element == null)
return appendNull(); Class<?> klass = element.getClass(); // klass 对于基本类型将自动包装
if (klass.equals(Integer.class)) {
int value = (Integer) element;
return append(value);
} if (klass.equals(Character.class)) {
char value = (Character) element;
return append(value);
} if (klass.equals(Boolean.class)) {
boolean value = (Boolean) element;
return append(value);
} if (klass.equals(Byte.class)) {
byte value = (Byte) element;
return append(value);
} if (klass.equals(Short.class)) {
short value = (Short) element;
return append(value);
} if (klass.equals(Long.class)) {
long value = (Long) element;
return append(value);
} if (klass.equals(Float.class)) {
float value = (Float) element;
return append(value);
} if (klass.equals(Double.class)) {
double value = (Double) element;
return append(value);
} // 非基本类型元素,调用其自身的hashCode方法进行计算
mHashCode = mHashCode * mMultiplierFactor + element.hashCode();
return this;
} /**
* 处理null值对象
* @return
*/
private HashCodeBuilder appendNull() {
mHashCode = mHashCode * mMultiplierFactor + 0;
return this;
}
}
java重写equals和hashCode方法的更多相关文章
- Java 重写equals()与hashCode()方法
List对象的contains方法实际上也是调用的equals()方法来进行逐条对比的. 示例代码: package com.imooc.collection; /** * 课程类 */ public ...
- Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)
Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例 原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...
- 为什么要重写equals和hashcode方法
equals hashcode 当新建一个java类时,需要重写equals和hashcode方法,大家都知道!但是,为什么要重写呢? 需要保证对象调用equals方法为true时,hashcode ...
- 如何正确的重写equals() 和 hashCode()方法
比较两个Java对象时, 我们需要覆盖equals和 hashCode. public class User{ private String name; private int age; priva ...
- Java:重写equals()和hashCode()
Java:重写equals()和hashCode() 1.何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). 2.设计equals() [1]使用instan ...
- java中equals和hashCode方法随笔二
前几天看了篇关于java中equals和hashCode方法的解析 1.Object类中的equals方法和hashCode方法. Object类中的equals和hashCode方法简单明了,所有的 ...
- 【转】Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例
原文地址:http://www.cnblogs.com/luankun0214/p/4421770.html 感谢网友的分享,记录下来只为学习. 1.重写equals方法实例 部分代码参考http ...
- Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例
1.重写equals方法实例 部分代码参考http://blog.csdn.net/wangloveall/article/details/7899948 重写equals方法的目的是判断两个对象 ...
- 内存泄漏避雷!你真的了解重写equals()和hashcode()方法的原因吗?
基本概念 要比较两个对象是否相等时需要调用对象的equals() 方法: 判断对象引用所指向的对象地址是否相等 对象地址相等时, 那么对象相关的数据也相等,包括: 对象句柄 对象头 对象实例数据 对象 ...
随机推荐
- Bootstrap栅格系统基本使用
1.什么是栅格系统: 在Bootstrap中,它提供了一套响应式.移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列.栅格系统用于通过一系列的行(row ...
- STM32F4编程手册学习2_内存模型
STM32F4编程手册学习2_内存模型 1. 内存映射 MCU将资源映射到一段固定的4GB可寻址内存上,如下图所示. 内存映射将内存分为几块区域,每一块区域都有一个定义的内存类型,一些区域还有一些附加 ...
- ZOJ 3229 Shoot the Bullet(有源汇的上下界最大流)
Description Gensokyo is a world which exists quietly beside ours, separated by a mystical border. It ...
- 单源最短路——SPFA算法(Bellman-Ford算法队列优化)
spfa的算法思想(动态逼近法): 设立一个先进先出的队列q用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路 ...
- Thunder团队第一周 - Scrum会议4
Scrum会议4 小组名称:Thunder 项目名称:爱阅app Scrum Master:代秋彤 工作照片: 参会成员: 王航:http://www.cnblogs.com/wangh013/ 李传 ...
- TCP/IP 三次握手四次挥手
TCP运输连接 TCP连接建立过程中要解决以下三个问题: (1)要使每一方能够确知双方的存在. (2)要允许双方协商一些参数(如最大窗口值.是否使用窗口扩大选项和时间戳选项以及服务质量等). (3)能 ...
- Qt-排序
1.要求传入起始指针,总长度,单元素空间占用大小(sizeof(A[i])),判断函数. 判断函数参数类型为const void *,使用需要在函数内自行转换为对应类型, 返回值为整数型,升序排序时正 ...
- HDU 2133 What day is it
http://acm.hdu.edu.cn/showproblem.php?pid=2133 Problem Description Today is Saturday, 17th Nov,2007. ...
- SQL SERVER 存储过程中SELECT 返回值如何赋值给变量
今天在处理一个问题时,使用到一个存储过程,是用于更新并获取最新ID的.在使用过程中,需要获取到这个ID并赋值给变量,结果用EXEC @ID = 存储过程的方式获取失败了.具体情况如下: 为了还原整个情 ...
- OBJ文件
OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模 ...