Java 散列集笔记
散列表
散列表(hash table)为每个对象计算一个整数,称为散列码(hash code)。 若需要自定义类,就要负责实现这个类的hashCode方法。注意自己实现的hashCode方法应该与equals方法兼容,即如果a.equals(b)为true,a与b必须具有相同的散列码。
hashCode方法
散列码是由对象导出的一个整型值,散列码是没有规律的,即若x与y是两个不同的对象,二者的散列码基本不会相同。 String类用下列算法计算散列码:
int hash = 0;
for (int i = 0; i < length(); i++)
hash = 31 * hash + charAt(i);
由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。 hashCode方法应该返回一个整型数值(也可以是负数),并合理地组合实例域的散列码,一边能够让各个不同的对象产生的散列码更均匀。 需要组合多个散列值时,可以调用Objects.hash并提供多个参数。这个方法会对各个参数调用Objects.hashCode,并组合这些散列值。
static int hash(Object... Objects)
返回一个散列码,由提供的所有对象的散列码组合而得到。
散列码应该能够快速地计算出来,并且这个计算只与要散列的对象状态有关,与散列表中其它对象无关。
Java中的散列表实现
Java中散列表用链表数组实现,每个列表被称为桶(bucket)。要想查找table中对象的位置,就要先计算它的散列码,然后与桶的总数取余,得到的就是保存这个元素的桶的索引。 如果bucket中没有其他元素,此时将元素直接插入bucket中就可以了;如果bucket中有元素,需要用新对象与该bucket中所有的对象进行比较,查看这个对象是否已经存在,不存在则修改链表结点索引加入bucket;如果bucket被占满,此现象被称为散列冲突(hash collision),此时需要用新对象与该bucket中所有的对象进行比较,查看这个对象是否已经存在。
桶数设置
若想更多地控制散列表的运行性能,就要指定一个初始的桶数。桶数是指用于收集具有相同散列值的桶的数目。 如果大致知道最终会有多少个元素要插入到散列表中,就可以设置桶数。通常将桶数设置为预计元素个数的75% ~ 150%。 有些研究人员认为,最好将桶数设置为一个素数,以防键的集聚。
设有哈希H(c) = c % N 取N为合数N = 2 ^ 3 = 8。 H(11100)= H(36)= 4; H(10100)= H(28)= 4; c的二进制第四位不参与运算,即无论取何值都不影响计算结果。 这样H(c)无法完整地反映c的特性,增大导致冲突的几率。
此外,实际中往往关键字有某种规律,例如大量的等差数列,那么公差和模数不互质的时候发生碰撞的概率会变大,而用质数可以在很大程度上回避这个问题,基本可以保证c的每一位都参与c的运算,从而在常见应用中减少冲突。
若散列表太慢,就需要再散列(rehashed)。需要创建一个桶数更多的表,并将所有元素插入到这个新表中,然后丢弃原来的表。装填因子决定何时对散列表进行再散列。一般0.75比较合理,即表中超过75%的位置已经填入元素时,这个表就会用双倍的桶数自动地进行再散列。
HashMap在根据用户传入的capacity计算得到默认容量,并不考虑load factor的因素,而是直接计算出第一个大于这个数字的2的幂。
设置默认容量可以参考JDK8中putAll的实现,即若明确知道HashMap中元素的个数,计算expectedSize / 0.75F + 1.0F是一个在性能上相对比较好的选择,但同时也会牺牲部分内存。
HashMap中使用HashMap(int initialCapacity)来实现。
参考:https://mp.weixin.qq.com/s/SFss68LcQc5ZFGpu-Ssgog
散列冲突的解决方法
开放地址法 当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。 公式:Hi=(H(key)+di) MOD m i=1,2,…,k (k <= m 1), H(key)为key的直接哈希地址,m为哈希表的长度,di为每次再探测时的地址增量。 增量di可以有不同的取法,并根据其取法有不同的称呼: ( 1 ) d i = 1 , 2 , 3 , …… 线性探测再散列; ( 2 ) d i = 1^2 ,- 1^2 , 2^2 ,- 2^2 , k^2, -k^2…… 二次探测再散列; ( 3 ) d i = 伪随机序列 伪随机再散列; 注意:对于利用开放地址法处理冲突所产生的哈希表中删除一个元素时需要谨慎,不能直接地删除,因为这样将会截断其他具有相同哈希地址的元素的查找地址,所以,通常采用设定一个特殊的标志以示该元素已被删除。
链地址法 如果散列表空间为 0 ~ m – 1 ,设置一个由 m 个指针分量组成的一维数组 ST[ m ], 凡散列地址为 i 的数据元素都插入到头指针为 ST[ i ] 的链表中。这种方法有点近似于邻接表的基本思想,且这种方法适合于冲突比较严重的情况。
再哈希法 当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突。 缺点:计算时间增加。
HashTable实现的数据结构
散列表可以用于实现几个重要的数据结构,其中最简单的是set类型。 Java集合类库中提供了一个HashSet类。散列集迭代器将依次访问所有的桶,由于散列将各个元素分散在表的各个位置上,所以访问它们的顺序几乎是随机的。
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable{
private transient HashMap<E,Object> map;
...
public HashSet() { map = new HashMap<>(); }
...
}
contains方法已经被重新定义,用于快速地查看是否某个元素已经出现在集中。它只需在某个桶中查找元素。
//HashSet的contains方法源码(借助HashMap的方法)
public boolean contains(Object o) {
return map.containsKey(o);
}
//来自HashMap的源码
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
public boolean containsKey(Object key) { //被HashSet的contains方法调用
return getNode(hash(key), key) != null;
}
参考: 《Core Java》
Java 散列集笔记的更多相关文章
- Java学习笔记(2)----散列集/线性表/队列/集合/图(Set,List,Queue,Collection,Map)
1. Java集合框架中的所有实例类都实现了Cloneable和Seriablizable接口.所以,它们的实例都是可复制和可序列化的. 2. 规则集存储的是不重复的元素.若要在集合中存储重复的元素, ...
- java 散列与散列码探讨 ,简单HashMap实现散列映射表运行各种操作示列
java 散列与散列码探讨 ,简单HashMap实现散列映射表运行各种操作示列 package org.rui.collection2.maps; /** * 散列与散列码 * 将土拔鼠对象与预报对象 ...
- java 散列
原文:https://www.cnblogs.com/younghao/p/8333795.html 为什么要设计散列这种数据结构呢?在现实世界中,实体之间可能存在着映射关系(key-value),比 ...
- java 散列运算浅分析 hash()
文章部分代码图片和总结来自参考资料 哈希和常用的方法 散列,从中文字面意思就很好理解了,分散排列,我们知道数组地址空间连续,查找快,增删慢,而链表,查找慢,增删快,两者结合起来形成散列 ...
- Java散列和散列码的实现
转自:https://blog.csdn.net/al_assad/article/details/52989525 散列和散列码 ※正确的equals方法应该满足的的条件: ①自反性:x.equ ...
- 数据结构与算法分析java——散列
1. 散列的概念 散列方法的主要思想是根据结点的关键码值来确定其存储地址:以关键码值K为自变量,通过一定的函数关系h(K)(称为散列函数),计算出对应的函数值来,把这个值解释为结点的存储地址,将结点存 ...
- 【C/C++】散列/算法笔记4.2
先说一下我自己的理解. 我先给你N组数据,这个N组里可能有重复的! 然后我们先统计好了N组里面的独立的每个对应的出现了几次(相当于map,然后每项属性有出现了多少次的),用的是数组下标对应 现在我们给 ...
- Java 对字符串数据进行MD5/SHA1哈希散列运算
Java对字符串数据进行MD5/SHA1哈希散列运算 [java] view plain copy package cn.aibo.test; import java.security.Message ...
- Java 消息摘要 散列 MD5 SHA
package xxx.common.util; import java.math.BigInteger; import java.security.MessageDigest; import jav ...
随机推荐
- 退役记——CCC2020&CCO2020
我叫吴佳诚,一个曾在福建师大附中就读的oier,2019年7月份我来到多伦多就读于Langstaff Secondary School 我的常用id有:Johnson_Wu,温词 竞赛经历: 2018 ...
- webApi前端ajax调用后端返回{"readyState":0,"status":0,"statusText":"error"}解决方案
var url = data.url, params = data.params, try_times = data.try_times , async = data.sync == 'false' ...
- idea中创建maven的Javaweb工程并进行配置
学完maven后,可以创建maven的javaweb工程,在创建完成后还需要一些配置,下面来说下具体步骤,在这里我创建的是一个模块,创建web项目的方式和创建模块一样 1.创建一个模块,点new-Mo ...
- 【笔记】机器学习 - 李宏毅 - 7 - Deep Learning
深度学习发展历史: 感知机和逻辑回归很像,只是没有\(sigmoid\)激活函数. 深度学习训练的三个步骤: Step1:神经网络(Neural network) Step2:模型评估(Goodnes ...
- MySQL 8 服务器组件
MySQL 服务器包含了一个基于组件的架构,用于扩展服务器功能.服务器和其他组件可以使用组件提供的服务.(在使用服务方面,服务器也是一个组件,等同于其他组件).组件之间交互仅通过他们各自提供的服务. ...
- 关于牛客网C语言结构体位域(bit-fields)的一道题
题目链接地址: https://www.nowcoder.com/questionTerminal/f4e20747a2dd4649bac0c028daa234f4 来源:牛客网 低地址字节 Byte ...
- es6简单小复习
Ecmascript 6 ECMAScript 6.0(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了. Ecmascript 是 JavaScript 语言的 ...
- 洛谷【P5004 专心OI - 跳房子】 题解
题目链接 https://www.luogu.org/problem/P5004 洛谷 P5004 专心OI - 跳房子 Imakf有一天参加了PINO 2017 PJ组,他突然看见最后一道题 他十分 ...
- 使用hexo+github搭建免费个人博客详细教程(转载)
https://www.cnblogs.com/liuxianan/p/build-blog-website-by-hexo-github.html 1.上传文档的hexo常用命令 2.输入hexo ...
- ubuntu16.04更换成国内源
一.首先使用自带的vi编辑器 安装完ubuntu后没有默认的root密码,如果要设置root密码需要进行如下步骤: 1 sudo passwd 2 连续输入两次新密码 二.vi编辑器下使用[backs ...