关于覆盖Object中的hashCode, equals和toString
最近在看《Effective Java》,里面看到了关于重载hashCode、equals和toString方法的篇章,顿时觉得视野开拓了不少,而且正结合自己工作、项目中的实例,觉得有必要总结一下,并分享给其它人。
首先,我准备了一个Bean,里面有几种数据类型的变量,算是各自举了个例子:
public class Instance {
public byte parameter1;
public boolean parameter2;
public char parameter3;
public short parameter4;
public int parameter5;
public long parameter6;
public float parameter7;
public double parameter8;
public int[] intArr;
public String string;
}
首先是toString吧,这个方法比较独立。toString方法的通用约定要求对本类提供一个“简洁且内容丰富”字符类型。如果没有重写toString的话,调用该方法并打印出来,字符串是这个样子的:
Instance@d5682s78
其中@前面的字符表示该类的名称,同时还会包括该类的包名。@后面的则是该类在内存中的位置地址的16进制表示。
使用Eclipse的应该都知道,source-->Generate toString。按照这种方式书写的toString方法如下:
@Override
public String toString() {
return "Instance [parameter1=" + parameter1 + ", parameter2="
+ parameter2 + ", parameter3=" + parameter3 + ", parameter4="
+ parameter4 + ", parameter5=" + parameter5 + ", parameter6="
+ parameter6 + ", parameter7=" + parameter7 + ", parameter8="
+ parameter8 + ", intArr=" + Arrays.toString(intArr)
+ ", string=" + string + ", hashcode=" + hashcode + "]";
}
这是覆盖toString方法的非格式化方式。覆盖toString有两种方式,接下来是一种格式化的方式:
@Override
public String toString() {
return String
.format("Instance [parameter1=%d, parameter2=%b, parameter3=%c, parameter4=%x, parameter5=%o, parameter6=%d, parameter7=%f, parameter8=%a, intArr=%s, string=%s]",
parameter1, parameter2, parameter3, parameter4,
parameter5, parameter6, parameter7, parameter8,
Arrays.toString(intArr), string);
}
其中的格式化符号的含义如下:
转换符 说明 示例
%s 字符串类型 "mingrisoft"
%c 字符类型 'm'
%b 布尔类型 true
%d 整数类型(十进制)99
%x 整数类型(十六进制)FF
%o 整数类型(八进制)77
%f 浮点类型 99.99
%a 十六进制浮点类型 FF.35AE
%e 指数类型 9.38e+5
%g 通用浮点类型(f和e类型中较短的)
%h 散列码
%% 百分比类型 %
%n 换行符
%tx 日期与时间类型(x代表不同的日期与时间转换符
当然在格式化的时候格式由开发人员确定,同时一旦确定就最好不要再次修改,因为这样会导致已有产品与后期的更新不匹配的情况。
然后下面是equals方法和hashCode方法。在Object的通用约定中,有以下的原则:
1,生成的同一个对象,多次调用hashCode,返回的值是一样;同样类型的对象,在多次运行时,产生的值可以不同。
2,equals返回true的对象,两者的hashCode返回值是相同的。
3,hashCode返回值相同的对象,equals未必返回true。
因此,在重写equals方法的同时,必须重写hashCode方法。
重写equals方法时,要求两个对象逻辑上相等。但未必在内存中的位置相等,亦即未必是相同的对象才能equals为true。通常equals方法比较类中的一些或全部变量在值上相等。下面是一个equals重写的示例:
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj instanceof Instance) {
Instance instance = (Instance) obj;
return instance.parameter1 == parameter1
&& instance.parameter2 == parameter2
&& instance.parameter3 == parameter3
&& instance.parameter4 == parameter4
&& instance.parameter5 == parameter5
&& instance.parameter6 == parameter6
&& instance.parameter7 == parameter7
&& instance.parameter8 == parameter8
&& instance.string.equals(string);
}
return false;
}
这个重写equals示例中,选取了parameter1-8和string作为是否相等的判断元素之一。
因为覆盖equals方法,必须要覆盖hashCode方法,而且,hashCode的计算中,equals中参与equals判断的元素必须出现在hashCode的计算过程中。
因而,hashCode的覆盖可以采用以下的方式:
@Override
public int hashCode() {
// TODO Auto-generated method stub
int result = 17;
result = 31 * result + parameter1;
result = 31 * result + (parameter2 ? 1 : 0);
result = 31 * result + parameter3;
result = 31 * result + parameter4;
result = 31 * result + parameter5;
result = 31 * result + (int) (parameter6 ^ (parameter6 >>> 32));
result = 31 * result + Float.floatToIntBits(parameter7);
long dLong = Double.doubleToLongBits(parameter8);
result = 31 * result + (int) (dLong ^ (dLong >>> 32));
result = 31 * result + string.hashCode();
return result;
}
这里hashCode的计算有几个通用的原则:
1,result的初始值为17。其实这个值可以随意取,但最好是素数。
2,参数因子31。之所以选择31,原因有二:一,31是一个传统的奇数。二,31*x==x<<5-x. 这两者决定了31作为hash计算时的参数因子,而且,很多的hash计算方法都会采用31。
3,对于byte, char, short,int等小整型,通常要将其转化为int类型跟result相加。
3,对于boolean类型,通常要转化为b ? 1:0;
4,对long类型,通常要进行(int)(f^(f>>>32))转换。
5,对于float类型,通常要进行Float.floatToIntBits(f)转化;
6,对于double类型,通常要进行Double.doubleToLongBits(d),之后再按照4进行转化后与result相加;
7,对于String类型,则要进行s.hashCodd()转化。
8,对于数组,则要对数组中的每一个元素都要进行相应的转化。
9,对于类类型,则要进行o.hashCode()转化。同时,该类类型的hashCode也要进行覆盖。
同时,这种计算hash的方法,参数的顺序不会对值产生影响;字符串中的字符顺序也不会产生影响。在实践中,这种hash计算方式具有较好的散列性质。
然而,这种方式覆盖的hashCode在每次调用时,都会进行一次计算,显然有些浪费资料并延缓了程序的运行。自然,这种方式还有等改进的地方。
下面的这种方式采用了预存hash值的方式:
private volatile int hashcode = 0;
@Override
public int hashCode() {
// TODO Auto-generated method stub
int result = hashcode;
if (hashcode == 0) {
result = 17;
result = 31 * result + parameter1;
result = 31 * result + (parameter2 ? 1 : 0);
result = 31 * result + parameter3;
result = 31 * result + parameter4;
result = 31 * result + parameter5;
result = 31 * result + (int) (parameter6 ^ (parameter6 >>> 32));
result = 31 * result + Float.floatToIntBits(parameter7);
long dLong = Double.doubleToLongBits(parameter8);
result = 31 * result + (int) (dLong ^ (dLong >>> 32));
result = 31 * result + string.hashCode();
}
return result;
}
这种方式预存了一个volatile类型的hash值,只有在hashCode方式在第一次调用时进行了hash值的计算,其余的时候只要直接获取hash值就可以了,明显地提高了程序的运行效率。
在这种计算hash的方法中,volatile关键字原义是“不稳定、变化”的意思,volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的。由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
最后,也可以对hashCode进行“延迟加载”,由于“延迟加载”通常伴随着代码上复杂性的增加和可读性的损失,因而不再对此方法赘述。
关于覆盖Object中的hashCode, equals和toString的更多相关文章
- java中的==、equals()、hashCode()源码分析
转载自:http://www.cnblogs.com/xudong-bupt/p/3960177.html 在Java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际 ...
- java中的==、equals()、hashCode()源码分析(转载)
在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. == java中的==是比较两个对象在JVM中的地址.比较好理解.看下面的代码: ...
- java中的==、equals()、hashCode()
java中的==.equals().hashCode()源码分析 在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. == java中 ...
- Object中的clone方法
Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象 ...
- 【Java基础之Object类(一)】Java中Object类中的所有方法(toString、equals、hashCode、clone、finalize、wait和notify等)详解(转载)
java中的hashcode.equals和toString方法都是基类Object的方法. 首先说说toString方法,简单的总结了下API说明就是:返回该对象的字符串表示,信息应该是简明但易于读 ...
- java中的hashcode()和equals()
equals()和hashcode()都继承自object类. equals() equals()方法在object类中定义如下: public boolean equals(Object obj) ...
- Java中的hashCode() 和 equals()的若干问题解答
一.hashCode()的作用 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int ...
- java 中为什么重写 equals 后需要重写 hashCode
本文为博主原创,未经允许不得转载: 1. equals 和 hashCode 方法之间的关系 这两个方法都是 Object 的方法,意味着 若一个对象在没有重写 这两个方法时,都会默认采用 Objec ...
- K:java中的hashCode和equals方法
hashCode和equals方法是Object类的相关方法,而所有的类都是直接或间接的继承于Object类而存在的,为此,所有的类中都存在着hashCode和equals.通过翻看Object类 ...
随机推荐
- 手机号ID开关星号(*)
.h文件 <span style="font-size:18px;">/** * 转成星号工具 */ @interface AsteriskTool : NSObjec ...
- [2013山东ACM]省赛 The number of steps (可能DP,数学期望)
The number of steps nid=24#time" style="padding-bottom:0px; margin:0px; padding-left:0px; ...
- Android使用surface直接显示yuv数据(三)
在本文中,Java创建UI和关节JNI经营层surface直接显示yuv数据(yv12).发展环境Android 4.4,驰A23平台. package com.example.myyuvviewer ...
- Linux:闪光的宝石,智慧(下一个)
2005年4月7日.Linus Torvalds公布了一款新型通用工具软件包,叫做"Git"(the Git source code management system).&quo ...
- HTTPS背后的加密算法(转)
当你在浏览器的地址栏上输入https开头的网址后,浏览器和服务器之间会在接下来的几百毫秒内进行大量的通信.InfoQ的这篇文章对此有非常详细的描述.这些复杂的步骤的第一步,就是浏览器与服务器之间协商一 ...
- hardware_hp存储映射_方案
修改虚拟磁盘映射方式 每个刀片独立对应映射存储空间 这样就不会造成数据写入冲突, old new 步奏: 创建过程 lun号码 1-155 之间 第二步奏 最后 指定: 就ok了 2012年12月 ...
- 使用ArcGIS API for Silverlight + Visifire绘制地图统计图
原文:使用ArcGIS API for Silverlight + Visifire绘制地图统计图 最近把很久之前做的统计图又拿出来重新做了一遍,感觉很多时候不复习,不记录就真的忘了,时间是最好的稀释 ...
- 九度OJ 1068 球半径和数量 (模拟)
题目1068:球的半径和体积 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4797 解决:1696 题目描写叙述: 输入球的中心点和球上某一点的坐标,计算球的半径和体积 输入: 球的中心 ...
- hdu(2062)-Subset sequence 组合数学
意甲冠军:查找集合{1,2,3...n}第一m一个排列子. 收集的线索所行的大小. 例两个元素的排列子集合按字典树排列是:{1},{1,2},{2},{2,1}: 解法:一个一个元素来确定,每次把剩余 ...
- 批处理命令篇--配置免安装mysql 5.6.22, 以及1067错误的一个解决方法
mysql 服务启动出现1067错误的一个解决方法: 当服务启动出现1067错误时,可查看“windows 事件查看器”,发现类似错误提示 Can't find messagefile 'F:\ ...