hashCode

说明:

本系列博客是本人在工作中遇到的一些问题的整理,其中有些资料来源网络博客,有些信息来自出版的书籍,掺杂一些个人的猜想及验证,总结,主要目的是方便知识的查看,并非纯原创。本系列博客会不断更新。原创不容易,支持原创。对于参考的一些其他博客,会尽量把博客地址列在博客的后面,以方便知识的查看。

 

本篇博客可以看做是《Effective Java中文版2版》第三章(对于所有对象都通用的方法)第九条(覆盖equals时总要覆盖hashCode)的读书笔记,其中掺杂了一些个人的想法及验证。

 

我们来看下Object类中hashCode方法的实现:

 

public native int hashCode();

 

这是一个本地方法

 

java规范对其描述是这样的:

1、在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。

2、如果两个对象根据equals方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生相同的整数结果。

3、如果两个对象根据equals方法比较是不相等的,那么调用这两个对象中国任意一个对象的hashCode方法,不一定产生不同的整数结果。但是给不相等的对象产生不同的整数结果,有可能提高散列表的性能。

 

书中的一个例子:

/**

 * Created with IntelliJ IDEA.

 * User: yejunwu123@gmail.com

 * Date: 2014-08-20 13:27

 * Description:

 */

public class PhoneNumber {

    private short areaCode;

    private short prefix;

    private short lineNumber;

    public PhoneNumber(int areaCode,int prefix,int lineNumber){

        this.areaCode = (short)areaCode;

        this.prefix = (short)prefix;

        this.lineNumber = (short)lineNumber;

    }

}

此处没有覆盖hashCodeequals方法,看下测试:

/**

 * Created with IntelliJ IDEA.

 * User: yejunwu123@gmail.com

 * Date: 2014-08-19 23:41

 * Description:

 */

public class HashCodeTest {

    public static void main(String[] args) {

        PhoneNumber phoneNumber1 = new PhoneNumber(1,2,3);

        PhoneNumber phoneNumber2 = new PhoneNumber(1,2,3);

        System.out.println("phoneNumber1.hashCode:" + phoneNumber1.hashCode());

        System.out.println("phoneNumber2.hashCode:" + phoneNumber2.hashCode());

    }

}

控制台输出:

phoneNumber1.hashCode:24355087

phoneNumber2.hashCode:5442986

类的不同实例,他们的hashCode不同

 

我们来加上equals方法:

@Override   

public boolean equals(Object obj) {

    if (obj == this){

        return true;

    }

    if (!(obj instanceof PhoneNumber)){

        return false;

    }

    PhoneNumber p = (PhoneNumber)obj;

    return this.areaCode == p.areaCode

                && this.prefix == p.prefix

                && this.lineNumber == p.lineNumber;

}

 

看下如下测试:

public class HashCodeTest {

    public static void main(String[] args) {

        PhoneNumber phoneNumber1 = new PhoneNumber(1,2,3);

        PhoneNumber phoneNumber2 = new PhoneNumber(1,2,3);

        System.out.println("phoneNumber1.hashCode:" + phoneNumber1.hashCode());

        System.out.println("phoneNumber2.hashCode:" + phoneNumber2.hashCode());

        System.out.println("------------------");

        System.out.println("phoneNumber1.equals(phoneNumber2):" + phoneNumber1.equals(phoneNumber2));

        Map<PhoneNumber,String> map = new HashMap<PhoneNumber, String>();

        map.put(phoneNumber1,"zhangsan");

        System.out.println("map contains phoneNumber1:" + map.containsKey(phoneNumber2));

        String name = map.get(phoneNumber2);

        System.out.println("name:" + name);

    }

}

 

创建PhoneNumber 的两个实例,先比较他们的equals,然后将第一个实例加入hashMap,然后判断HashMapcontainsKey方法及get方法(入参是第二个实例),控制台输出如下:

phoneNumber1.hashCode:24355087

phoneNumber2.hashCode:5442986

------------------

phoneNumber1.equals(phoneNumber2):true

map contains phoneNumber1:false

name:null

 

导致这个原因是因为覆盖了equals方法,没有同时覆盖hashCode方法,HashMap实现在putgetcontainsKey等方法时会计算keyhash,具体HashMap的原理可以参看博客:

http://www.cnblogs.com/xwdreamer/archive/2012/06/03/2532832.html

 

已经讲得很详细了,不重复劳动了。

 

书中提出的一种简单的hashCode生成方式如下:

1、把某个非0的常数值,比如说17,保存在一个名为resultint类型的变量中;

2、对于对象中的每个关键域f(equals方法中涉及的每个域),完成以下步骤:

  a.为该域计算int类型的散列码c

    .如果该类型是boolean类型,则计算(f ? 1 : 0);

    .如果该域是bytecharshortint类型,则计算(int)f

    .如果该域是long类型,则计算(int)(f ^ (f >>> 32));

    .如果该域是float类型,则计算Float.floatToIntBits(f);

    .如果该域是double类型,则计算Double.doubleToLongBits(f),然后按照步骤

          2.a.,为得到的long类型的值计算散列值;

    .如果该域是一个对象引用,并且该类的equals方法通过递归的调用equals的方式

          来比较这个域,则同样为这个域递归的调用hashCode,如果这个域的值为null,

          返回0

    .如果该域是一个数组,则要把每一个元素当做单独的域来处理。如果数组域中 

          的每个元素都很重要,可以使用Arrays.hashCode方法。

  b.按照如下公式,2.a中计算得到的散列码c合并到result中:

      result = 31 * result + c;

3、返回result

以上规则能记住当然最好,但是工作中我们用的IDE往往为我们提供了这些功能,以下代码是在IDEA中自动生成的:

@Override

public int hashCode() {

    int result = (int) areaCode;

    result = 31 * result + (int) prefix;

    result = 31 * result + (int) lineNumber;

    return result;

}

 

跟以上规则是类似的。

 

再来运行下之前的测试:

phoneNumber1.hashCode:1026

phoneNumber2.hashCode:1026

------------------

phoneNumber1.equals(phoneNumber2):true

map contains phoneNumber1:true

name:zhangsan

 

我们来看下如下的测试:

/**

 * Created with IntelliJ IDEA.

 * User: yejunwu123@gmail.com

 * Date: 2014-08-20 15:25

 * Description:

 */

public class PhoneNumberTest {

    public static void main(String[] args) {

        PhoneNumber phoneNumber1 = new PhoneNumber(1,2,3);

 

        Map<PhoneNumber,String> map = new HashMap<PhoneNumber, String>();

        map.put(phoneNumber1,"zhangsan");

 

        System.out.println("map contains PhoneNumber(1,2,3) before update:" + map.containsKey(new PhoneNumber(1,2,3)));

 

        System.out.println("----------------------");

 

        for (PhoneNumber phoneNumber : map.keySet()){

            System.out.println("before update hash code:" + phoneNumber.hashCode());

            phoneNumber.setAreaCode((short) 4);

            System.out.println("after update hash code:" + phoneNumber.hashCode());

        }

 

        for (PhoneNumber phoneNumber : map.keySet()){

            System.out.println(phoneNumber.getAreaCode() + " " + phoneNumber.getPrefix() + " " + phoneNumber.getLineNumber() + " " + phoneNumber.hashCode());

        }

 

        System.out.println("map contains PhoneNumber(1,2,3) after update:" + map.containsKey(new PhoneNumber(1,2,3)));

        System.out.println("##############");

        System.out.println("map contains PhoneNumber(4,2,3) after update:" + map.containsKey(new PhoneNumber(4,2,3)));

    }

}

 

phoneNumber1 加入HashMap,然后判断map.containsKey(new PhoneNumber(1,2,3)),然后对HashMap遍历,修改keyareacode属性,以改变keyhashCode,然后再去判断map.containsKey(new PhoneNumber(1,2,3))map.containsKey(new PhoneNumber(4,2,3))

 

看下输出结果:

map contains PhoneNumber(1,2,3) before update:true

----------------------

before update hash code:1026

after update hash code:3909

4 2 3 3909

map contains PhoneNumber(1,2,3) after update:false

##############

map contains PhoneNumber(4,2,3) after update:false

 

后面两次比较都是返回false

原因:

for (PhoneNumber phoneNumber : map.keySet()){           

    System.out.println("before update hash code:" + phoneNumber.hashCode());

    phoneNumber.setAreaCode((short) 4);

    System.out.println("after update hash code:" + phoneNumber.hashCode());

}

 

这段代码执行过后,因为mapkeyareaCode值已经修改,在判断map.containsKey(new PhoneNumber(1,2,3)),由于equals方法返回false导致contains方法返回false;而在判断map.containsKey(new PhoneNumber(4,2,3))返回false是因为mapkeyhashCodenew PhoneNumber(4,2,3)hashCode值不一样,这点在前文提到的博客中有说明,map中的entry中的key会缓存计算后的hash,详细信息可以参看上文提到的博客。

 

hashCodeequals方法还是很重要的,在理解hibernate的数据库一致性与实体一致性时有涉及到。

数据中心机房是现代信息技术的核心设施,它承载着企业的重要数据和服务,因此,其基础设计与规划至关重要。在制定这样的方案时,需要考虑的因素繁多,包括但不限于以下几点: 1. **容量规划**:必须根据业务需求预测未来几年的数据处理和存储需求,合理规划机房的规模和设备容量。这涉及到服务器的数量、存储设备的容量以及网络带宽的需求等。 2. **电力供应**:数据中心是能源消耗大户,因此电力供应设计是关键。要考虑不间断电源(UPS)、备用发电机的容量,以及高效节能的电力分配系统,确保电力的稳定供应并降低能耗。 3. **冷却系统**:由于设备密集运行,散热问题不容忽视。合理的空调布局和冷却系统设计可以有效控制机房温度,避免设备过热引发故障。 4. **物理安全**:包括防火、防盗、防震、防潮等措施。需要设计防火分区、安装烟雾探测和自动灭火系统,设置访问控制系统,确保只有授权人员能进入。 5. **网络架构**:规划高速、稳定、冗余的网络架构,考虑使用光纤、以太网等技术,构建层次化网络,保证数据传输的高效性和安全性。 6. **运维管理**:设计易于管理和维护的IT基础设施,例如模块化设计便于扩展,集中监控系统可以实时查看设备状态,及时发现并解决问题。 7. **绿色数据中心**:随着环保意识的提升,绿色数据中心成为趋势。采用节能设备,利用自然冷源,以及优化能源管理策略,实现低能耗和低碳排放。 8. **灾难恢复**:考虑备份和恢复策略,建立异地灾备中心,确保在主数据中心发生故障时,业务能够快速恢复。 9. **法规遵从**:需遵循国家和地区的相关法律法规,如信息安全、数据保护和环境保护等,确保数据中心的合法运营。 10. **扩展性**:设计时应考虑到未来的业务发展和技术进步,保证机房有充足的扩展空间和升级能力。 技术创新在数据中心机房基础设计及规划方案中扮演了重要角色。例如,采用虚拟化技术可以提高硬件资源利用率,软件定义网络(SDN)提供更灵活的网络管理,人工智能和机器学习则有助于优化能源管理和故障预测。 总结来说,一个完整且高效的数据中心机房设计及规划方案,不仅需要满足当前的技术需求和业务目标,还需要具备前瞻性和可持续性,以适应快速变化的IT环境和未来可能的技术革新。同时,也要注重经济效益,平衡投资成本与长期运营成本,实现数据中心的高效、安全和绿色运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值