java实现自定义哈希表
哈希表实现原理
哈希表底层是使用数组实现的,因为数组使用下标查找元素很快。所以实现哈希表的关键就是把某种数据类型通过计算变成数组的下标(这个计算就是hashCode()函数***
怎么把一个字符串转化成整数下标呢?
- 可以把每个字符的ASCII对应的数字相加作为下标,比如"abc"=(a-96)+(b-96)+(c-96),'a'的ASCII是97;这种方式的缺点就是哈希值很容易重复,比如aaa,abc,cab
- 也可以使用幂的连乘,保证不同字符串算出来的哈希值不一样,这种方式的缺点是会算出来的哈希值会发生数值越界
- 解决越界问题可以使用大数运算,java里的BitInt
实现
首先创建数据类型
package dataS.hash;
import java.util.Objects;
public class Info {
//员工号
private String key;
//员工值
private String value;
public Info(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "Info{" +
"key='" + key + '\'' +
", value='" + value + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Info info = (Info) o;
return Objects.equals(key, info.key) &&
Objects.equals(value, info.value);
}
}
创建HashTable类
package dataS.hash;
import java.math.BigInteger;
public class HashTable {
private Info[] arrays;
/**
* 默认构造方法,默认数组大小100
*/
public HashTable() {
this.arrays = new Info[100];
}
/**
* 指定大小
*/
public HashTable(int maxsize){
this.arrays=new Info[maxsize];
}
/**
* 插入数据,直接把员工号作为数组索引
*/
public void insert(Info info){
this.arrays[hashCode(info.getKey())]=info;
}
/**
* 查找数据,直接把员工号作为数组索引
*/
public Info find(String key){
return arrays[hashCode(key)];
}
public int hashCode(String key){
return hash3(key);
}
public int hash1(String key){
//1.将字母转化成ASCII,然后相加
int hashvalue=0;
for (int i = 0; i < key.length(); i++) {
//a是97,其他字母减去97就是字母对应的数字
int letter=key.charAt(i)-96;
hashvalue+= letter;
}
//取模可以压缩可选值,比如想把100个可选择压缩到0-9,对数组长度取模
return hashvalue%arrays.length;
}
public int hash2(String key){
//2.幂的连乘,这里可能hashvalue的值超过范围,使用long也不行,可以用bigint
int hashvalue=0;
int pow27=1;
for (int i = 0; i < key.length(); i++) {
//比如abc,1*27^0+2*27^1+2*27^2
int letter=key.charAt(i)-96;
hashvalue+= letter*pow27;
pow27*=27;
}
return hashvalue%arrays.length;
}
public int hash3(String key){
//3.用bigint
BigInteger hashvalue=new BigInteger("0");
BigInteger pow27=new BigInteger("1");
for (int i = 0; i < key.length(); i++) {
//比如abc,1*27^0+2*27^1+3*27^2
int letter=key.charAt(i)-96;
//把letter用bigint包装起来
BigInteger bigLetter=new BigInteger(letter+"");
hashvalue=hashvalue.add(bigLetter.multiply(pow27));
pow27=pow27.multiply(new BigInteger(27+""));
}
return hashvalue.mod(new BigInteger(arrays.length+"")).intValue();
}
}
测试
package dataS.hash;
public class HashTest {
public static void main(String[] args) {
HashTable hashTable=new HashTable();
hashTable.insert(new Info("a","111"));
hashTable.insert(new Info("tc","222"));
hashTable.insert(new Info("cba","333"));
System.out.println(hashTable.find("a").getValue());
System.out.println(hashTable.find("tc").getValue());
System.out.println(hashTable.find("cba").getValue());
}
}
发现tc把a的位置给占用了
冲突解决
为什么会有冲突呢?因为我压缩了可选值(进行了取模运算),比如我想把1和101个元素放到大小为10的数组,对10取模后下标都是1,肯定会发生冲突
开放地址法
把一号位置占用了,101就看2号位置有没有被占用,直到找到空位置,然后插入。主要101原本想插入的位置和最终插入位置一定是连续的,中间不会有空位置
修改插入删除和查找方法
package dataS.hashTwo;
import java.math.BigInteger;
public class HashTable {
private Info[] arrays;
/**
* 默认构造方法,实现哈希表的本质是哈希函数将不同类型的数据转化成数组下表
*/
public HashTable() {
this.arrays = new Info[100];
}
/**
* 指定大小
*/
public HashTable(int maxsize){
this.arrays=new Info[maxsize];
}
/**
* 插入数据,直接把员工号作为数组索引
*/
public void insert(Info info){
String key=info.getKey();
//关键字对应的哈希值,将要作为下标
int hashValue=hash3(key);
//如果被占用,并且key对应的value也不为空(因为删除的时候是删除info对象里的value,而不是全部)
while (arrays[hashValue]!=null&&arrays[hashValue].getValue()!=null){
//一直找到一个没被占用的
hashValue++;
//比如99和599哈希值取模后都是99,99加1后数组会越界,但是前面还有空的位置
hashValue%=arrays.length;
//直到整个数组都填满
}
arrays[hashValue]=info;
}
/**
* 查找数据,直接把员工号作为数组索引
*/
public Info find(String key){
int hashValue=hash3(key);
//从第一次的位置,到最终插入位置是连续的
while (arrays[hashValue]!=null){
//如果key值相等说明找到了
if(arrays[hashValue].getKey().equals(key))
return arrays[hashValue];
hashValue++;
hashValue%=arrays.length;
}
return null;
}
public Info delete(String key){
int hashValue=hash3(key);
//从第一次的位置,到最终插入位置是连续的
while (arrays[hashValue]!=null){
//如果key值相等说明找到了,将Info的value值空
if(arrays[hashValue].getKey().equals(key)){
Info info=arrays[hashValue];
arrays[hashValue].setValue(null);
return info;
}
hashValue++;
hashValue%=arrays.length;
}
return null;
}
public int hashCode(String key){
return hash3(key);
}
public int hash1(String key){
//1.将字母转化成ASCII,然后相加
int hashvalue=0;
for (int i = 0; i < key.length(); i++) {
//a是97,其他字母减去97就是字母对应的数字
int letter=key.charAt(i)-96;
hashvalue+= letter;
}
//取模可以压缩可选值,比如想把100个可选择压缩到0-9,对数组长度取模
return hashvalue%arrays.length;
}
public int hash2(String key){
//2.幂的连乘,这里可能hashvalue的值超过范围,使用long也不行,可以用bigint
int hashvalue=0;
int pow27=1;
for (int i = 0; i < key.length(); i++) {
//比如abc,1*27^0+2*27^1+2*27^2
int letter=key.charAt(i)-96;
hashvalue+= letter*pow27;
pow27*=27;
}
return hashvalue%arrays.length;
}
public int hash3(String key){
//3.用bigint
BigInteger hashvalue=new BigInteger("0");
BigInteger pow27=new BigInteger("1");
for (int i = 0; i < key.length(); i++) {
//比如abc,1*27^0+2*27^1+3*27^2
int letter=key.charAt(i)-96;
//把letter用bigint包装起来
BigInteger bigLetter=new BigInteger(letter+"");
hashvalue=hashvalue.add(bigLetter.multiply(pow27));
pow27=pow27.multiply(new BigInteger(27+""));
}
return hashvalue.mod(new BigInteger(arrays.length+"")).intValue();
}
}
测试
发现冲突问题解决了
链地址法:
实现原理,将一个个链表作为数组的元素,当发生冲突就将冲突元素链接到对应的链表后面
不同的哈希值对应一个不同的链表,哈希值相同的串在一起
链表结构,实现增删查功能
public class LinkedNode {
public Info info;
public LinkedNode next;
public LinkedNode(Info info) {
this.info = info;
}
}
package dataS.hashThree;
public class Linked {
public LinkedNode head;
public Linked() {
head = null;
}
//插入节点,在头结点之后插入.
//重点是不要让节点丢失
public void insert(Info info) {
LinkedNode node = new LinkedNode(info);
if (head == null) {
head = node;
} else {
node.next = head.next;
// head.next=node;此处不应该这么写,会形成环
head.next = node;
}
}
//在头结点之后删除一个元素
public LinkedNode delete() throws Exception {
if(head.next==null){
head=null;
return null;
}else{
LinkedNode tmp = head.next;
head.next = tmp.next;
return tmp;
}
}
//查找方法
public LinkedNode find(String key) {
LinkedNode tmp = head;
if(tmp==null)
return null;
while (!key.equals(tmp.info.getKey())){
if(tmp.next==null)
return null;
tmp=tmp.next;
}
return tmp;
}
//根据值来删除元素
public LinkedNode deleteByvalue(String key){
LinkedNode ans = null;
LinkedNode pretmp = head;
LinkedNode tmp =head.next;
if(key.equals(head.info.getKey())){
ans=head;
head=head.next;
return ans;
}
while (tmp!=null){
if(key.equals(tmp.info.getKey())){
ans=tmp;
pretmp.next=tmp.next;
}
pretmp=pretmp.next;
tmp=tmp.next;
}
return ans;
}
}
哈希表
package dataS.hashThree;
import java.math.BigInteger;
public class HashTable {
private Linked[] arrays;
/**
* 默认构造方法,实现哈希表的本质是哈希函数将不同类型的数据转化成数组下表
*/
public HashTable() {
this.arrays = new Linked[100];
}
/**
* 指定大小
*/
public HashTable(int maxsize){
this.arrays=new Linked[maxsize];
}
/**
* 插入数据,直接把员工号作为数组索引
*/
public void insert(Info info){
String key=info.getKey();
//关键字对应的哈希值,将要作为下标
int hashValue=hash3(key);
//
if(arrays[hashValue]==null){
arrays[hashValue]=new Linked();
}
//插入
arrays[hashValue].insert(info);
}
/**
* 查找数据,直接把员工号作为数组索引
*/
public Info find(String key){
int hashValue=hash3(key);
//从第一次的位置,到最终插入位置是连续的
LinkedNode node = arrays[hashValue].find(key);
if(node==null)
return null;
return node.info;
}
public Info delete(String key){
int hashValue=hash3(key);
//从第一次的位置,到最终插入位置是连续的
LinkedNode node = arrays[hashValue].deleteByvalue(key);
return node.info;
}
public int hashCode(String key){
return hash3(key);
}
public int hash1(String key){
//1.将字母转化成ASCII,然后相加
int hashvalue=0;
for (int i = 0; i < key.length(); i++) {
//a是97,其他字母减去97就是字母对应的数字
int letter=key.charAt(i)-96;
hashvalue+= letter;
}
//取模可以压缩可选值,比如想把100个可选择压缩到0-9,对数组长度取模
return hashvalue%arrays.length;
}
public int hash2(String key){
//2.幂的连乘,这里可能hashvalue的值超过范围,使用long也不行,可以用bigint
int hashvalue=0;
int pow27=1;
for (int i = 0; i < key.length(); i++) {
//比如abc,1*27^0+2*27^1+2*27^2
int letter=key.charAt(i)-96;
hashvalue+= letter*pow27;
pow27*=27;
}
return hashvalue%arrays.length;
}
public int hash3(String key){
//3.用bigint
BigInteger hashvalue=new BigInteger("0");
BigInteger pow27=new BigInteger("1");
for (int i = 0; i < key.length(); i++) {
//比如abc,1*27^0+2*27^1+3*27^2
int letter=key.charAt(i)-96;
//把letter用bigint包装起来
BigInteger bigLetter=new BigInteger(letter+"");
hashvalue=hashvalue.add(bigLetter.multiply(pow27));
pow27=pow27.multiply(new BigInteger(27+""));
}
return hashvalue.mod(new BigInteger(arrays.length+"")).intValue();
}
public int hash4(String key){
//3.使用开放地址法解决冲突
//当冲突发生,查找空位置插入,而不再用哈希函数得到数组下标
return 1;
}
}
测试
发现冲突解决了
总结
哈希表的本质是数组,学会hashCode的实现方式,数据的压缩,掌握解决冲突的俩种办法
重点是链地址法,比开放地址法高效简洁
java实现自定义哈希表的更多相关文章
- JAVA数据结构之哈希表
Hash表简介: Hash表是基于数组的,优点是提供快速的插入和查找的操作,编程实现相对容易,缺点是一旦创建就不好扩展,当hash表被基本填满的时候,性能下降非常严重(发生聚集引起的性能的下降),而且 ...
- 使用java实现希表的基础功能
用java代码完成哈希表数据结构的简单实现, 以公司雇员的添加修改作为模拟实例 具体代码如下: package com.seizedays.hashtable; import java.util.Sc ...
- Java中的哈希
Java中的哈希 前言 在开发中经常用到HashMap.HashSet等与哈希有关的数据结构,一直只知道这些哈希的数据结构不保证顺序,不清楚具体什么情况.所以在这里大致总结一下. Java的Has ...
- 哈希表(hashtable)的javascript简单实现
javascript中没有像c#,java那样的哈希表(hashtable)的实现.在js中,object属性的实现就是hash表,因此只要在object上封装点方法,简单的使用obejct管理属性的 ...
- Java学习笔记31(集合框架五:set接口、哈希表的介绍)
set接口的特点: 1.不包含重复元素 2.set集合没有索引,只能用迭代器或增强for循环遍历 3.set的底层是map集合 方法和Collection的方法基本一样 set接口的实现类HashSe ...
- 自己动手实现java数据结构(五)哈希表
1.哈希表介绍 前面我们已经介绍了许多类型的数据结构.在想要查询容器内特定元素时,有序向量使得我们能使用二分查找法进行精确的查询((O(logN)对数复杂度,很高效). 可人类总是不知满足,依然在寻求 ...
- Java学习:Set接口与HashSet集合存储数据的结构(哈希表)
Set接口 java.util.Set接口 extends Collection接口 Set接口的特点: 不允许存储重复的元素 没有索引,没有带索引的方法,也不能使用普通的for循环遍历 java.u ...
- 146. LRU 缓存机制 + 哈希表 + 自定义双向链表
146. LRU 缓存机制 LeetCode-146 题目描述 题解分析 java代码 package com.walegarrett.interview; /** * @Author WaleGar ...
- Java基础知识笔记(一:修饰词、向量、哈希表)
一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...
随机推荐
- CentOS忘记mariadb/mysql root密码解决办法
本文不再更新,可能存在内容过时的情况,实时更新请访问原地址:CentOS忘记mariadb/mysql root密码解决办法: 这里有两种方式实现修改mariadb root密码. mariadb版本 ...
- 音视频开发-FFmpeg
音视频开发是个非常复杂的,庞大的开发话题,初涉其中,先看一下结合 OEIP(开源项目) 新增例子. 可以打开flv,mp4类型文件,以及rtmp协议音视频数据,声音的播放使用SDL. 把采集的麦/声卡 ...
- I - 动物狂想曲 HDU - 6252(差分约束)
I - 动物狂想曲 HDU - 6252 雷格西桑和路易桑是好朋友,在同一家公司工作.他们总是一起乘地铁去上班.他们的路线上有N个地铁站,编号从1到N.1站是他们的家,N站是公司. 有一天,雷格西桑起 ...
- 1272: 【基础】求P进制数的最大公因子与最小公倍数
1272: [基础]求P进制数的最大公因子与最小公倍数 时间限制: 1 Sec 内存限制: 16 MB 提交: 684 解决: 415 [提交] [状态] [讨论版] [命题人:外部导入] 题目描述 ...
- PTA数据结构与算法题目集(中文) 7-19
PTA数据结构与算法题目集(中文) 7-19 7-19 求链式线性表的倒数第K项 (20 分) 给定一系列正整数,请设计一个尽可能高效的算法,查找倒数第K个位置上的数字. 输入格式: 输入首先给 ...
- 修复Windows10引导,适用gpt+uefi环境
在双硬盘多系统安装时,容易损坏Win10的开机引导文件. 可以尝试用Windows原版安装盘启动,进入命令提示符模式: 首先使用diskpart命令确认需要修复的Windows分区的安装卷X:. 再运 ...
- Serverless无服务器云函数入门唠叨
B站录了个视频: https://www.bilibili.com/video/av59020925/
- PTA 6-1 单链表逆转
本题是一个非常经典的题目:单链表逆转. 这是链表结点的定义: typedef struct Node *PtrToNode; struct Node { ElementType Data; /* 存储 ...
- 15-场景中用到的资源监视器(perfmon metrics collector)
JMeter 无法提取除 Tomcat 之外的其他服务器的指标,因此PerfMon Metrics Collector可用来获取性能数据. PerfMon Metrics Collector使用的是S ...
- leetcode c++做题思路和题解(5)——堆的例题和总结
堆和优先队列 堆的简介, 是一种二叉树, 有最大堆和最小堆miniheap. 通常用于构建优先队列. 0. 目录 数据流中的第K大元素 1. 数据流中的第K大元素 数据流中的第K大元素 复杂度为log ...