最近在看《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的更多相关文章

  1. java中的==、equals()、hashCode()源码分析

    转载自:http://www.cnblogs.com/xudong-bupt/p/3960177.html 在Java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际 ...

  2. java中的==、equals()、hashCode()源码分析(转载)

    在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. ==  java中的==是比较两个对象在JVM中的地址.比较好理解.看下面的代码: ...

  3. java中的==、equals()、hashCode()

    java中的==.equals().hashCode()源码分析 在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. ==  java中 ...

  4. Object中的clone方法

      Java中对象的创建 clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象 ...

  5. 【Java基础之Object类(一)】Java中Object类中的所有方法(toString、equals、hashCode、clone、finalize、wait和notify等)详解(转载)

    java中的hashcode.equals和toString方法都是基类Object的方法. 首先说说toString方法,简单的总结了下API说明就是:返回该对象的字符串表示,信息应该是简明但易于读 ...

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

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

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

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

  8. java 中为什么重写 equals 后需要重写 hashCode

    本文为博主原创,未经允许不得转载: 1. equals 和 hashCode 方法之间的关系 这两个方法都是 Object 的方法,意味着 若一个对象在没有重写 这两个方法时,都会默认采用 Objec ...

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

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

随机推荐

  1. Display Database Image using MS SQL Server 2008 Reporting Services

    原文 Display Database Image using MS SQL Server 2008 Reporting Services With the new release of MS SQL ...

  2. C语言功能 --C

    功能名称: cabs 动力 能够: 计算绝对复数值 使用 法国: double cabs(struct complex z); 程序示例: #include <stdio.h> #incl ...

  3. 创建和分析excel文件

    jxl.jar:下载地址:http://download.csdn.net/detail/xuxu198899223/7717737 package excel; public class BookV ...

  4. Android监视返回键

    android在发展中,监视键返回到后事件经常被用来,在下面的例子来说明什么android返回键事件监听器. public class BackKeyTest extends Activity { / ...

  5. EpPlus读取生成Excel帮助类+读取csv帮助类+Aspose.Cells生成Excel帮助类

    大部分功能逻辑都在,少量自定义异常类和扩展方法 ,可用类似代码自己替换 //EpPlus读取生成Excel帮助类+读取csv帮助类,epplus只支持开放的Excel文件格式:xlsx,不支持 xls ...

  6. Java串口通信详细解释

    前言 说到开源.恐怕非常少有人不挑大指称赞. 学生通过开源码学到了知识,程序猿通过开源类库获得了别人的成功经验及可以按时完毕手头的project,商家通过开源软件赚到了钱……,总之是皆大欢喜. 然而开 ...

  7. 利用缓存、Timer间隔时间发送微信的实例,很有用的例子

    //Class WechatOfferExcutor 此类为微信触发类,属于上层调用类,其中有用到用静态变量缓存offer信息,Task异步执行发送方法等 using Newtonsoft.Json. ...

  8. C# 中的常用正则表达式总结

    这是我发了不少时间整理的C# 的正则表达式 ,新手朋友注意一定要手册一下哦,这样可以节省很多写代码的时间,中国自学编程网为新手朋友整理发布. 只能输入数字:"^[0-9]*$". ...

  9. 怎样用LINQ或EF生成NOT IN和IN语句

    例如:有一个问卷表Questionnaire和一个活动与问卷的关系表ActivityOption_Questionnaire,现在我们要找出不在活动中的问卷. 用lambda实现方法如下: var n ...

  10. maven_修改setting ,改为自己私服或者OSC开源中国 [为解决sqlite-jdbc 在中央仓库找不到]

    因为项目要使用到sqlite ,虽然有现成的jar,但是考虑的项目的易用统一管理,决定还是用maven 结果纠结了半天 sqlite-jdbc 在maven默认的仓库根本找不着,于是乎修改 setti ...