哈希表:将对象转换为索引,然后存储在数组中。

定义注意点:

  • 对象:就是面向对象中的对象,可以为任何东西。整数、浮点数、日期、字符串、类。
  • 转换:通过hash函数来完成,hash函数是hash表的核心与难点。对于整数,可以将取模运算作为hash函数
  • 数组:hash表本质是就是一个数组(静态、动态),这也是名称中"表"的含义。

体现的计算机思想: 空间换时间

思考角度,当空间无限时,可以使用O(1)完成各项操作,当空间只要1个时,就退化为线性表O(n)。

哈希表关注的核心问题

  • 哈希函数如何设计
  • 如何解决hash冲突

对于不同的关键字得到了同一个hash地址,这种现象称为hash冲突(collision),形式化为:key1≠key2,f(key1)==f(key2),其中f为hash函数。

hash函数的设计原则

  • 一致性:如果a==b,则hash(a)==hash(b),这是java自定义类时必须需重写的hashcode方法原因。

  • 高效性:计算高效便捷,O(1),这也是使用动态数组,在适当的情况下resize的原因。

  • 均匀性哈希值的分布越均匀越好这就是取模法中模为质数的原因

整数转换为索引的方法:取模法

hashcode=val%M,其中M为一个质数,M的参考取值请点击这儿。注意,公式总val为正整数,如果类型为int,可以先进行去除符号操作:val=val&ox7fffffff。因为从二进制的角度看ox7fffffff就是0和31个1,正好把符号位过滤掉。

任何对象都可以表示为整数。

  • 浮点数:在计算机内部都是用32位或者64位二进制表示,从整数的角度去解析这些位,就找到了浮点数对应的整数。
  • 字符串:字符串本质上可以理解为B(base)进制数,其中B可以是不同字符串的个数。例如26。也可以是任意设定的一个质数。
    • 例如:code=c*26^3+o*26^2+d*26^1+e*26^0
    • 例如:abcd=a*B^3+b*B^2+c*B^1+d*B^0

进制表示的形式简化以及编程实现:

hash(code)=(c*B^3+o*B^2+d*B^1+e*B^0)%M,可以表示为每一位乘以base,在加下一位

=((((c*B+o)*B+d)*B+e)%M,很重要,在java字符串的hashcode方法中B=31

=((((c%M)*B+o)%M*B+d)%M*B+e)%M,取余操作可以拿到括号里面去。(此性质快速幂算法中很常用)

int hash=0;
for(int i=0;i<s.length;i++){
hash=(hash*B+s.charAt(i))%M;
}
//java中B的是31,不在乎是否溢出,只要返回的是一个整数就OK,不知道M是什么,所以就没有出现M。
  • 日期类型考虑每个部分,每部分表示不同的权重(进制思维)。

    • Date: year,month,day,则hash(date)=(((date.year%M)*B+date.month%M)*B+date.day)%M
  • 分别将类的每一个字段当做B进制中的某一位。依据B进制数进行转换。

当将自定义的类作为hashmap和hashSet的Key时,必须重写hashcode方法和equal方法。

1.因为默认的hashcode()方法取对象的地址为基础获得的,而new()同一类的不同实例对象地址不同,使得hashcode的结果也不同,这就不满足一致性,例如,new Person("小明")两次,它们的hashcode不同,但这显然就不合理。

2.重写hashcode()只是为了获得正确的hash值,但当冲突了,还需要逐个字段进行比较才能确定是否相等,这就要求重写equal来完成,因为默认的equal就等于==,含义为比较对象地址。

自定义hashcode和equals的实例

基本思路利用已有基本类型的包装类和String类的hashcode()方法来生成我们的hashcode()

public class Student {
Integer grade;
Integer cls;
String name;
//省去无关代码
@Override
public int hashCode() {//套路:模仿String,Base取31
int B=31;
int hash=0;
hash=hash*B+grade.hashCode();
hash=hash*B+cls.hashCode();
hash=hash*B+name.hashCode();
return hash;
} @Override
public boolean equals(Object obj) {//有套路,逐个字段比较
if(this==obj) return true;
if(obj==null)return false;
if(this.getClass()!=obj.getClass()) return false;
Student another=(Student) obj;
return this.grade.equals(another.grade) &&
this.cls.equals(another.cls) &&
this.name.equals(another.name);//字符串比较相等,equals
}
}

完整代码以及测试用例,请点击这儿

hash冲突的解决方法:链地址法

数组中每个元素保留的是地址。数组中每个元素的位置是N%M

去掉符号位:hashcode(k1)&0X7FFFFFFF

动态空间(扩容和缩容)处理N/M>=upperTolN/M<lowerTol

实现自己的hashtable,采用TreeMap作为链接冲突元素的容器

都是先获得key索引,然后再获某个元素。TreeMap<K,V> map=hashtable[hash(key)]

完整源码及测试代码请点击这儿

更多关于hash冲突的办法

  1. 开放地址法。
  2. 再哈希法:rehashing.

似乎是最实用的hashtable知识总结的更多相关文章

  1. Java重点之小白解析--浅谈HashMap与HashTable

    这是一个面试经常遇到的知识点,无论什么公司这个知识点几乎是考小白必备,为什么呢?因为这玩意儿太特么常见了,常见到你写一百行代码,都能用到好几次,不问这个问哪个.so!本小白网罗天下HashMap与Ha ...

  2. Linux系统一本通(实用篇)

    本人最近一直在ubuntu,接下来和大家分享我曾经踩过的坑,和一些非常实用的命令知识- 安装中的磁盘分配 一般来说,在linux系统中都有最少两个挂载点,分别是/ (根目录)及 swap(交换分区), ...

  3. MySQL索引知识介绍

    前言: 索引是MySQL数据库中的重要对象之一,索引的目的在于提高查询效率.可以类比字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可.索引是表的目录,在查找内容之前可以先 ...

  4. 6个冷门但实用的pandas知识点

    1 简介 pandas作为开展数据分析的利器,蕴含了与数据处理相关的丰富多样的API,使得我们可以灵活方便地对数据进行各种加工,但很多pandas中的实用方法其实大部分人都是不知道的,今天就来给大家介 ...

  5. 疯狂Java学习笔记(84)----------大约 Java 对象序列化,你不知道 5 事

    几年前,.当一个软件团队一起用 Java 书面申请.我认识比一般程序猿多知道一点关于 Java 对象序列化的知识所带来的优点. 关于本系列 您认为自己懂 Java 编程?其实,大多数程序猿对于 Jav ...

  6. 常见电子元器件检测方法。——Arvin

    电子设备中使用着大量各种类型的电子元器件,设备发生故障大多是由于电子元器件失效或损坏引起的.因此怎么正确检测电子元器件就显得尤其重要,这也是电子维修人员必须掌握的技能.我在电器维修中积累了部分常见电子 ...

  7. CCF NOI系列活动

    NOI-全国青少年信息学奥林匹克竞赛全国青少年信息学奥林匹克竞赛(NOI)是国内信息学领域内面向中学生的最高水平的大赛,每省派经选拔产生的选手(其中一名是女选手)参加,NOI每年在不同的省市举行. N ...

  8. 第二篇 顾问实施ERP与医生看病过程类比

    我从08年开始涉足企业信息化咨询行业.当时做的第一个项目是汕头某黄金珠宝公司的SAP ERP业务优化项目.我在项目上担任顾问助理的角色.我们公司的老板据说是以前ORACLE公司华南区的总监,后来是格兰 ...

  9. ASP.NET MVC 4高级编程(第4版)

    <ASP.NET MVC 4高级编程(第4版)> 基本信息 作者: (美)Jon Galloway    Phil Haack    Brad Wilson    K. Scott All ...

随机推荐

  1. 区间dp入门+例题

    区间dp作为线性dp的一种,顾名思义是以区间作为阶段进行dp的,使用它的左右端点描述每个维度,决策往往是从小状态向大状态转移中推得的.它跟st表等树状结构有着相似的原理---向下划分,向上递推. dp ...

  2. VulnHub靶场学习_HA: ARMOUR

    HA: ARMOUR Vulnhub靶场 下载地址:https://www.vulnhub.com/entry/ha-armour,370/ 背景: Klaw从“复仇者联盟”超级秘密基地偷走了一些盔甲 ...

  3. 你知道如何自动保存 Spring Boot 应用进程号吗

    1. 前言 欢迎阅读 Spring Boot 2 实战 系列文章. PID 对于系统运维来说并不陌生,但是对于一些开发者特别是新手还是要简单介绍一下的.它是 Process ID 的简称,是系统分配给 ...

  4. python 3 的解释器

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:Yangtze PS:如有需要Python学习资料的小伙伴可以加点击下 ...

  5. vue2.x学习笔记(二十三)

    接着前面的内容:https://www.cnblogs.com/yanggb/p/12639440.html. 渲染函数&JSX 基础 vue推荐在绝大多数的情况下使用模板来创建html.然而 ...

  6. net core天马行空系列:降低net core门槛,数据库操作和http访问仅需写接口,实现类由框架动态生成

    引文   hi,大家好,我是三合.不知各位有没有想过,如果能把数据库操作和http访问都统一封装成接口(interface)的形式, 然后接口对应的实现类由框架去自动生成,那么必然能大大降低工作量,因 ...

  7. CDNbest-访问限制

    写在开始之前 有时候我们需要对网站某个目录或整站限制只对特点的ip访问 不是在ip范围之内的统统拒绝 步骤 首先我们登录,找到我们的目标站点,点击去如下图所示 找到高级设置 添加新规则,如下 这里用到 ...

  8. 基于tcp协议的套接字通信:远程执行命令

    要解决粘包问题: TCP:流式协议 特点: 1.数据流没有开头也没有结果,像水流一样 2.TCP协议有一个nagle算法, nagle算法会将数据量较小,并且时间间隔较短的数据合成一条数据发送, 这么 ...

  9. 关于go的init函数

    亲测,如果加载一个包,如果一个包里的每个文件,均含有init函数,那么均会执行. 目前来看,init的执行顺序,是文件名称的自然排序进行执行的. 并且只是所加载包里的go文件的init函数执行,对于包 ...

  10. STM32 内存分配解析及变量的存储位置

    内存映射 在一些桌面程序中,整个内存映射是通过虚拟内存来进行管理的,使用一种称为内存管理单元(MMU)的硬件结构来将程序的内存映射到物理RAM.在对于 RAM 紧缺的嵌入式系统中,是缺少 MMU 内存 ...