九、散列与散列码

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                         

Java编程思想——第17章 容器深入研究 读书笔记(四)的更多相关文章

  1. Java编程思想——第17章 容器深入研究 读书笔记(三)

    七.队列 排队,先进先出. 除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: ad ...

  2. Java编程思想——第17章 容器深入研究 读书笔记(二)

    五.List的功能方法 排除Collection已包含的方法外还增加了 boolean addAll(int index, Collection<? extends E> c);从索引位置 ...

  3. Java编程思想——第17章 容器深入研究 读书笔记(一)

    这一章将学习散列机制是如何工作的,以及在使用散列容器时怎么样编写hashCode()和equals()方法. 一.容器分类 先上两张图 来概况完整的容器分类 再细说都为什么会有那些特性. 二.填充容器 ...

  4. Java编程思想——第17章 容器深入研究(two)

    六.队列 排队,先进先出.除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: add ...

  5. Java编程思想——第17章 容器深入研究(一)

    这一章将学习散列机制是如何工作的,以及在使用散列容器时怎么样编写hashCode()和equals()方法. 一.容器分类 先上两张图 来概况完整的容器分类 再细说都为什么会有那些特性. 二.Coll ...

  6. Java编程思想(11~17)

    [注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第十一章 持有对象 11.1 泛型和类型安全的容器>eg: List<St ...

  7. Java编程思想 第21章 并发

    这是在2013年的笔记整理.现在重新拿出来,放在网上,重新总结下. 两种基本的线程实现方式 以及中断 package thread; /** * * @author zjf * @create_tim ...

  8. Java编程思想——第14章 类型信息(一)

    运行时类型信息使得你可以在程序运行时发现和使用类型信息.Java是如何让我们在运行时识别对象和类的信息得呢? 主要有两种方式:1.传统RTTI,他假定我们在编译期间已经知道了所有类型:2.反射,它允许 ...

  9. Java编程思想(第一章 对象入门)总结

    面向对象编程(oop) 1.1抽象的进步 所有编程语言的最终目的都是提供一种“抽象”方法.   难点是 在机器模型(位于“方案空间”)和实际解决问题模型(位于“问题空间”)之间,程序员必须建立起一种联 ...

随机推荐

  1. netcore mvc 的简单实现

    实现的功能 简单的路由系统 支持中间件 简单Filter支持 只支持HttpPost.HttpGet 使用Dotliquid做为视图渲染引擎 核心实现 HttpChannel 复制监听Tcp请求,并按 ...

  2. Docker下Jedis体验

    jedis是redis的java版本的客户端实现,本文通过一些web请求&响应的实例展示了jedis的基本用法: 开始编码前我们先把环境准备好,总共两个server,对应两个docker容器: ...

  3. FreeSql (二十九)Lambda 表达式

    FreeSql 支持功能丰富的表达式函数解析,方便程序员在不了解数据库函数的情况下编写代码.这是 FreeSql 非常特色的功能之一,深入细化函数解析尽量做到满意,所支持的类型基本都可以使用对应的表达 ...

  4. 彻底解决android拍照后无法显示的问题

    这是对上篇"android 图片拍照,相册选图,剪切并显示"的文章之后的 改进 上一篇文章虽然能解决图片的拍照剪切以及显示,但是发现他有一个缺点, 如果该程序单独运行,貌似没有任何 ...

  5. java架构之路-(JVM优化与原理)JVM之G1回收器和常见参数配置

    过去的几天里,我把JVM内部的垃圾回收算法和垃圾回收器.还剩下最后一个G1回收器没有说,我们今天数一下G1回收器和常见的参数配置. G1回收器 G1 (Garbage-First)是一款面向服务器的垃 ...

  6. Spark学习之Scala的基础知识

    Scala的变量声明 在Scala创建变量的时候,必须使用val或者var val,变量值不可修改,一旦分配不能重新指向别的值 var,分配后,可重新指向类型相同的值 举例 val lines = s ...

  7. 四、springBoot 优雅的创建定时任务

    前言 好几天没写了,工作有点忙,最近工作刚好做一个定时任务统计的,所以就将springboot 如何创建定时任务整理了一下. 总的来说,springboot创建定时任务是非常简单的,不用像spring ...

  8. spring与logstash整合,并将数据传输到Elasticsearch

    logstash是一个开源的数据收集引擎,支持各种输入选择,能够同时从多个来源采集数据,将数据转发到想存储的“库”中,例如,可以转发存储到Elasticsearch,也可以转发到kafka等消息中间件 ...

  9. MySQL数据库忘记密码怎么办?

    忘记MySQL数据库密码就进不去数据库,也就无法修改密码,解决方法如下: 1:打开cmd命令符,先关闭正在运行的数据库,输入如下命令: 2:打开mysql.exe和mysqld.exe所在的文件夹,复 ...

  10. Oracle clob列union的方法(ORA-00932)

    今天在做“站内搜索”数据抽取时,为了能将多个相似的数据库表数据合并,使用了SQL中union关键字,期望将多个单独的SQL查询结果合并到一起.每个单独的SQL都能成功执行,在union合并的过程中遇到 ...