- 浏览: 336675 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
alafqq:
很好的一篇启蒙hashmap的文章;HASHTABLE的93行 ...
使用数组和链表实现hash表存储信息 -
小帅1127:
我擦,我还以为有什么大坑呢,这也写出来。。。
if..else if和if..if的区别 -
fncj:
转下http://www.dodoer.com
hadoop单机版搭建图文详解 -
yueshang520:
Spring注解原理的详细剖析与实现 -
fncj:
转下,谢谢http://www.whohelpme.com/b ...
Spring注解原理的详细剖析与实现
1、HashTable的原理:
通过节点的关键码确定节点的存储位置,即给定节点的关键码k,通过一定的函数关系H(散列函数),得到函数值H(k),将此值解释为该节点的存储地址. <!--EndFragment-->
简而言之, 哈希表之所以能够实现根据关键字来获取记录, 是因为它在内部建立了记录存储位置 - 即内部数组中的索引号和关键字的一套对应关系H, 因而在查找时, 只需根据这个映射关系H 找到给定键值 K 对应的数H(K)就可直接从数组中取得目的数据,而不必对数组进行遍历和比较. 这个对应关系H我们称为哈希函数.
哈希函数 H的两个重要特点:
1>、哈希函数可以自定义, 只要使得整数 H(K) 的范围不超出哈希表内部存储数组的上下界即可。以下代码即使用了键值index=K%table.length的方法。
2>、K 的取法有任意种, 但 H(K) 只能固定在一个范围, 因此不同的关键字可能对应了相同的哈希值, 形成了冲突.
<!--EndFragment-->2、
3、冲突及解决方案
需要注意的是哈希函数的运算和冲突的处理都需要系统开销,尤其是后者代价不菲。因此产生了两个关键问题: 如何设计函数 H的算法, 以及如何处理冲突, 才能使得哈希表更加高效。
冲突解决技术可以分为两类:开散列方法(也称单练方法或者挂链方法)和闭散列方法(也称开地址法)。这两种方法不同之处与冲突记录的存储有关系。开散列方法将冲突记录在表外,即在表外挂上链表,闭散列方法这把冲突记录存储在表中另一个槽(数组)中。<!--EndFragment-->
开散列方法(挂链法)的一种简单形式把散列表中的每个槽(数组的每个存储地址)定义为一个链表的表头,散列到一个槽的所有记录(即hash索引值相同时)都放在该槽的链表中。以下代码即以这种方式解决了冲突。
闭散列冲突解决方法以及其他改进的冲突解决方案,这里就不详述了,一般的数据结构书上都有介绍。
4、HashTable综合了数组的高效查询和链表的高效增删改,以下是以数组和链表为基础实现的hash表代码。
个人信息类:Accout_info.java
public class Account_info { private String name; private String password; private String city; public Account_info(String name,String password,String city){ this.name=name; this.password=password; this.city=city; } public String toString(){ return name+" "+password+" "+city; } //get和set函数省略 }
信息节点类:Account_node.java
public class Account_Node { public Account_Node(int q_num,Account_info info){ keyQQ=q_num; acc_info=info; } public Account_Node next; public int keyQQ; public Account_info acc_info; public String toString(){ return keyQQ+" "+acc_info.toString(); } }
Hash表类:HashTable.java
public class HashTable { //基数组,hash表 private Account_Node[] table=new Account_Node[50]; //阈值 hash表存储的数据达到阈值,则rehash private float threshold=0.75F; //hash表中数据总数 private long len=0; //将账号信息放入hash表中 public void put(int q_num,Account_info acc_info){ //通过自定义hash函数获取索引值 int index=hashQ_num(q_num); /* * 判断是否超过阈值 * 如果超过,则reHash一次,capacity扩大 * 然后再放! */ if(isThreshold()){ int oldCapacity=table.length; Account_Node[] oldTab=table; int newCapacity=oldCapacity*2+1;//数组扩容 table=new Account_Node[newCapacity]; for(int i=0;i<oldCapacity;i++){ Account_Node node=oldTab[i]; while(node!=null){ int newIndex=hashQ_num(node.keyQQ); table[newIndex]=node; node=node.next; } } } //1、如果hash表中索引值对应的首个地址为空,即不存在冲突 if(table[index]==null){ table[index]=new Account_Node(q_num,acc_info); } //2、存在冲突,则将数据节点放入挂链中的最后一个节点后 else{ Account_Node node=table[index]; //找到链表node的最后一个节点 while(node.next!=null){ node=node.next; } //将账户信息节点插入存在index冲突的链表中的最后一个节点 node.next=new Account_Node(q_num,acc_info); } len++; } //从hash表中取出账号信息 public Account_Node get(int Q_num){ int index=hashQ_num(Q_num); Account_Node acc_Node=table[index]; //看是否有下了个节点,如有,则是冲突的,就要一个一个比较 if(table[index]==null) return null; while(acc_Node.next!=null){ if(acc_Node.keyQQ==Q_num){ return acc_Node; }else{ acc_Node=acc_Node.next; } } if(acc_Node.keyQQ!=Q_num) return null; return acc_Node; } //显示hash表 public void showTable(){ for(int index=0;index<table.length;index++){ System.out.print(index+"\t"); Account_Node node=table[index]; while(node!=null){ System.out.print(node.acc_info.getName()+"\t"); node=node.next; } System.out.println(""); } } //通过账号号码计算账号的has索引值,自定义hash函数 private int hashQ_num(int q_num){ int index=(int) (q_num%table.length); return index; } //判断hash表是否达到阈值 //己放信息节点的个数和table的长度比 private boolean isThreshold(){ if(len/table.length>=threshold) return true ; return false; } //获取存入hash表中信息的数量 public long TableLen(){ return this.len; } }
测试函数:Test.java
public class Test { /**测试函数 * @param args */ public static void main(String[] args) { HashTable hsT=new HashTable(); Account_info acc; for(int i=0;i<200;i++){ String name="Name"+i; String pwd="Pwd"+i; acc=new Account_info(name,pwd,"长沙"); hsT.put(i, acc); } acc=new Account_info("wxy","123","长沙"); hsT.put(108, acc); hsT.showTable(); Account_Node node=hsT.get(50); System.out.println(node); node=hsT.get(120); System.out.println(node); } }
疏漏偏颇之处还请高手指教!
评论
HASHTABLE的93行写错了;
我想应该是
if(len>table.length*threshold){
发表评论
-
apache日志信息详解
2011-11-06 21:19 6269一、访问日志的格式 Apache内建了记录服务器 ... -
浏览器如何工作
2011-08-19 08:57 0http://taligarsiel.com/Projects ... -
编码实现用JDK中的Proxy实现springAOP功能
2011-08-18 15:04 742http://blog.csdn.net/iamtheevil ... -
Spring注解原理的详细剖析与实现
2011-08-14 23:09 84233本文主要分为三部分: ... -
Spring装配基本属性的原理分析与代码实现
2011-08-11 15:37 1417首先,做一个配置属性的基本测试。修改beans.xml,使引用 ... -
编码剖析Spring依赖注入的原理
2011-08-10 20:01 1812一、注入依赖对象 基本类型对象注入: <b ... -
Spring的三种实例化Bean的方法
2011-08-10 14:03 1Spring的三种实例化Bean的方法 1、 使用 ... -
Spring管理bean的原理自定义实现
2011-08-10 10:44 61841、Spring通过BeanDefinition管理基于S ... -
spring环境搭建与测试
2011-08-10 08:40 3406Chapter1、搭建与测试spring的环境 1、 ... -
java回调机制实现
2011-08-08 09:06 2033Java的接口支持提供了一种获得回调的等价功能的 ... -
log4j的使用与详细分析
2011-08-05 13:32 2624一、什么是log4j? http://logging.a ... -
log4j使用详解
2011-08-04 23:05 2http://logging.apache.org/log4j ... -
java解析XML的四种方法的学习与比较
2011-03-30 20:55 7228四种XML解析方法: ... -
自定义日志模块实现
2011-03-30 09:58 1101package wxy.XXXX.Utils; impo ... -
synchronized(this)
2011-03-29 09:17 69361、当两个并发线程访问同一个对象object中的这个synch ... -
详细解析Java中抽象类和接口的区别(转)
2011-03-24 23:48 922在Java语言中, abstract cl ... -
NIO学习笔记(三)---通道
2011-03-09 23:06 15511、通道基础 ... -
NIO学习笔记(2)--缓冲区
2011-03-09 18:20 9351、一个Buffer对象是固定数量的数据的容器。其作用是 ... -
封锁管理子系统模拟实现java版
2011-03-09 18:01 1175封锁管理子系统模拟实现 文件锁定 ... -
NIO学习笔记(一)I/O缓冲区操作
2011-03-07 20:04 1228上图简单描述了数据从外部磁盘向运行中的进程的内存区域移动的 ...
相关推荐
HashMap是一个散列桶(数组和链表),它存储的内容是键值对(key-value)映射HashMap采用了数组和链表的数据结构,能在查询和修改方便继承了数组的线性查找和链表的寻址修改HashMap是非synchronized,所以HashMap很快...
Hash表采用了数组加链表的结构,即一个数组元组中不再是存储单个元素,而是存储一个链表,就好比包租婆收租的时候,一个握把上面挂了一连串的钥匙一样。Hash表的引出是为了减少查询数据库操作所带来的时间问题,将...
数组(Array):连续存储元素的集合,可以通过索引访问元素。插入和删除元素的时间复杂度较高,为 O(n),但...哈希表(Hash Table):使用哈希函数将键映射到存储位置的数据结构,可以实现快速的插入、删除和查找操作。
数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端。所有的数据结构都可以用这两个基本结构来构造的 数组:数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度...
从数据存储结构中:线性表,数组,链表,hash表讲起,引申到集合底层存储结构.到排序
存储结构在jdk1.7当中是数组加链表的结构,在jdk1.8当中改为了数组加链表加红黑树的结构。 HashMap在多线程的环境下是不安全的,没有进行加锁措施,所以执行效率快。如果我么需要有一个线程安全的HashMap,可以使用...
hashmap是一个key-value键值对的数据结构,从结构上来讲在jdk1.8...HashMap是用hash表来存储的,在hashmap里为解决hash冲突,使用链地址法,简单来说就是数组加链表的形式来解决,当数据被hash后,得到数组下标,把数
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。
HashMap内部采用数组和链表的方式存储数据,每个元素都包含键值对,通过hash函数将键映射到数组的索引位置,实现高效的查找和插入。 HashMap的性能优化策略。 HashMap在性能优化方面采取多种策略,如扩容机制、负载...
以数组为基本形态,数组中的元素先以链表形式储存,当链表的长 度超过8时(包含数组上的那个链表头)就会将链表转换为红黑树,以加快修改和查询效率。当然除了HashMap还 有很多地方都会用到红黑树,理解红黑树的原理...
比较和对比数组、链表和哈希表的属性和操作 定义哈希冲突以及如何处理它们 确定从哈希表中添加和删除元素的机制和复杂性 使用哈希表通过缓存(记忆和制表)提高时间复杂度 选择适合解决问题的工具的哈希表 第 1 部分...
首先,我们了解一下HashMap的底层结构历史,在JDK1.8之前采用的是数组+链表的数据结构来存储数据,是不是觉得很熟悉,没错这玩意在1.8之前的结构就和HashTable一样都是采用数组+链表,同样也是通过链地址法(这里简称...
hash函数:用的是time33的散列函数,将一个字符串的key转换成一个数字一个C数组:用来储存桶(buckets)的两个双向的链表:第一个双向链表是数组的每个元素(桶bucket)是一个双向链表,这样做是为了解决hash冲突;...
文章目录前言正文什么是散列表Hash的数据结构存储数据的数组散列函数Hash的负载因子开放寻址法链表法Hash结构的几个操作读操作开放寻址法的读操作链表法的读操作写操作开放寻址法的写操作链表法的写入扩容总结 ...
7.8 链表排序和索引表排序 7.9 内部排序小结 7.10 外部排序 7.11 参考文献和选读材料 第8章 Hash法 8.1 引言 8.2 静态Hash法 8.3 动态Hash法 8.4 Bloom滤波器 8.5 参考文献和选读材料 第9章 优先队列 9.1...
· 数组(静态数组、动态数组)、线性表、链表(单向链表、双向链表、循环链表)、队列、栈、树(二叉树、查找树、平衡树、线索树、线索树、堆)、图等的定义、存储和操作 · Hash(存储地址计算,冲突处理)的具体...
HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每一个Entry本质上是一个单向链表。当准备添加一个key-value对时,首先通过hash(key)方法计算hash值,然后通过indexFor(hash,length)求该key-...
字符串在java程序中被大量使用,为了避免每次都创建相同的字符串对象及内存分配,JVM内部对字符串对象的创建做了一定的优化,在Permanent Generation中专门有一块区域用来存储字符串常量池(一组指针指向Heap中的...
当两个数的余数相同时仅仅凭借余数作为下标来查找就会发生错误即hash冲突,那么链地址法其实就是将余数相同的数用链表储存起来,那么查找时就遍历余数对应的链表即可(类似邻接表) 题目出处 #include #include ...
数组和链表的存储方式有什么区别? 集合有哪些? ArrayList和LinkedList的区别? ArrayList的扩容机制? HashMap的原理?扩容机制? 解决Hash冲突的方法?除了开放寻址法和链表法还有吗? 散列函数有哪些?有什么优...