九、散列与散列码

HashMap使用equals()判断当前的键是否与表中存在的键相同。

正确的equals()方法需满足一下条件:

1)自反性。x.equals(x) 是true;

2)对称性。x.equalse(y) 返回true y.equals(x)也得是true;

3)传递性。x.equals(y) 返回true ,y.equals(z) 返回true , x.equals(z)返回true;

4)一致性。如果对象中用于等价比较的信息没有变,那么无论多少次 x.equals(y)返回值不会变

5)x.equals(null) 返回 false ;注意:(null).equals(x)报空指针。

强调:默认的Object.equals()只比较对象的地址,因此如果使用自己的类作为HashMap的Key,必须同时重载hashCode()和equals()。

hashCode()并不需要总是能够返回唯一的标识码,但是equals()方法必须严格的判断两个对象是否相等,作为键必须唯一否则系统报错。

    @Override
    public boolean equals(Object o) {
        return o instanceof T && (i.equals(((T) o).i)));
    }

instanceof检查了此对象是否为null ,是null则返回false。

为速度而散列

以线性查询的是最慢的查询方式,存储一组元素最快的数据结构是数组,所以使用它来标识键的信息(注意,这里说的是键信息不是键本身)。

由于数组不能调整容量,所以数组不保存键本身,而是通过键对象生成一个散列码,将其作为数组的下标,这个散列码就是由Object中的、或自己的类覆盖的hashCode()生成的。

数组固定的问题解决了,但是键可以产生相同的下标,也就是说可能会有冲突。数组多大不重要,任何键总能在数组中找到它的位置。

于是,查询一个值的过程首先就是计算散列码,然后使用散列码查找数组。如果能够保证没有冲突(如果被查询的值的数量是固定的,就有可能)。

通常,冲突由外部链接处理;数组并不直接保存值,而是保存值的list。然后对list中的值使用equals()方法进行线性查询。

这部分查找会比较慢,但是如果散列函数好的话,数组每个位置就有较少的值。

因此,不是查询整个List而是快速的跳转到数组的某个位置,只对很少的元素进行比较。这就是HashMap会如此快的原因。 

我们把散列表的数组称为bucket(桶),为了散布均匀且速度快,桶的容积通常使用质数或者2的整数次方,用LinkedList填充桶。

put()操作,计算key的hashCode(),找到桶中的位置,看LinkedList内容,有值用equals()与值的key相比,相等就替换,不等或者没有值就在尾部加上新值。

覆盖hashCode()

桶下标值是无法控制的,这个值依赖于具体的HashMap对象的容量,而容量的改变与容器的充满程度和负载因子有关。hashCode()生成的结果,经过畜栏里后成为桶位的下标。

Joshua Blochw指出为写出一份像样的hashCode给出了知道:

1)给 int 变量 result 赋予某个非0常量,

2)为对象内每个有意义的域f(既每个可以做equals()操作的域)计算一个int 散列码 c:

域类型:                                                                 计算:

boolean                                                                   c=(f?0:1)

byte、char、short、int                                           c=(int)f

float                                                                         c=(int)(f^(f>>>32))

double                                                                     long I =Double.doubleToLongBits(f); c=(int)(I^(I>>>32))

Object,其equals()调用这个域的equals()       c=f.hashCode()

数组                     对每个元素应用上述规则   

3)合并计算得到散列码

result = 37*result+c                         

最新文章

  1. 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载
  2. VFP笔记
  3. iOS7: 如何获取不变的UDID
  4. QOS
  5. linux取某个字段排重
  6. jQuery过滤选择器:not()方法使用介绍
  7. Navigation Bar上的返回按钮文本颜色,箭头颜色以及导航栏按钮的颜色
  8. linux find命令详解--转
  9. http://c7sky.com/works/css3slides/#1
  10. JavaSE学习总结第23天_多线程1
  11. year:2017 month:7 day:18
  12. Python 接口:从协议到抽象基类
  13. JavaSpcript基础
  14. java代码示例(6-3)
  15. 杀掉所有 skynet 进程
  16. Apche Kafka 的生与死 – failover 机制详解
  17. 关于Unity中协程、多线程、线程锁、www网络类的使用
  18. Maven发布war包到Tomcat
  19. 【TCP/IP详解 卷一:协议】第十七章 TCP:传输控制协议
  20. zoj1003-Max Sum (最大连续子序列之和)

热门文章

  1. 聊聊 Python 的单元测试框架(二):nose 和它的继任者 nose2
  2. Mysql中有符号数和无符号数的区别
  3. UVM——寄存器模型相关的一些函数
  4. 单线程Redis性能为何如此之高?
  5. Spring Boot(二) 配置文件
  6. 三段式有限状态机Verilog代码
  7. js中对时间的操作
  8. demo演示如何写一个无配置格式统一的日志
  9. CF #579 (Div. 3) B.Equal Rectangles
  10. 用 CocosCreator 快速开发推箱子游戏