重温数据结构:哈希表,MyHashMap与MyHashSet的Java实现

来源:互联网 发布:官方软件 编辑:程序博客网 时间:2024/06/06 14:07

一.哈希表的相关概念

哈希函数的构造方法:

1.      直接定址法

2.      数字分析法

3.      平法取中法

4.      折叠法

5.      除留余数法

6.      随机数法

二.处理冲突的方法

1.      开放定制法(线性探测再散列、二次探测再散列、伪随机探测再散列)

2.      再哈希法

3.      链地址法

4.      建立一个公共溢出区

三.MyHashMap的实现

要点:哈希函数的方法选用“除留余数法”,处理冲突的方法是用“链地址法”,也即是当发生冲突时,凡是哈希地址为i的记录,都插入到头指针为tables[i]的链表中,插入的位置可以是表头,也可以是表尾,也可以在中间(以保持key的有序)。

本次实现的MyHashMap是将其插入表头。

package edu.njupt.zhb;/* *@author: ZhengHaibo   *web:     http://blog.csdn.net/nuptboyzhb *mail:    zhb931706659@126.com *2014-3-25  Nanjing,njupt,China */public class MyHashMap<K,V>{/** * HashMap数组中的节点 * @param <K> * @param <V> */public class MyEntry<K,V>{ public K key;//键 public V value;//值 public MyEntry<K,V> next;//下一个结点}private MyEntry<K, V> []tables;private final int INIT_LEN=50;public MyHashMap() {tables=new MyEntry[INIT_LEN];}/** * 哈希函数的构造方法:除留余数法 * @param hashcode * @return */public int indexFor(int hashcode){return Math.abs(hashcode)%tables.length;}/** * 获取键为key的值 * @param key * @return */public V get(K key){if(key==null){return null;}int hashcode=key.hashCode();//计算键的哈希值int index=indexFor(hashcode);//根据哈希值计算其在表中的下标indexMyEntry<K, V> pNode=tables[index];//取出第一个节点,也即是链表的头//在链表中查找while(pNode!=null){//对链表进行遍历//当key的哈希值和真实值相等时返回if(pNode.key.hashCode()==hashcode&&(key==pNode.key || key.equals(pNode.key))){return pNode.value;}pNode=pNode.next;//链表:遍历下一个值}return null;}/** * 将键值对放入map当中 * 解决冲突的方法:链地址法 * @param key * @param value * @return */public V put(K key,V value){if(key==null){return null;}int hashcode=key.hashCode();int index=indexFor(hashcode);MyEntry<K, V> head=tables[index];//取出第一个节点,也即是链表的头MyEntry<K, V> pNode=head;//首先查找是否已经存在keywhile(pNode!=null){//当key的哈希值和真实值相等时if(pNode.key.hashCode()==hashcode&&(key==pNode.key || key.equals(pNode.key))){pNode.value=value;//更新当前key的值return pNode.value;}pNode=pNode.next;//链表:遍历下一个值}//如果没有查找到,将该值插入到链表的表头MyEntry<K, V> newNode=new MyEntry<K,V>();newNode.key=key;newNode.value=value;newNode.next=head;tables[index]=newNode;return value;}/** * 根据key删除对应的值 * 思路:先查找,后删除 * @param key * @return */public V remove(K key){if(key==null){return null;}int hashcode=key.hashCode();int index=indexFor(hashcode);MyEntry<K, V> head=tables[index];MyEntry<K, V> pNode=head;if(pNode==null){return null;}//表头if(pNode.key.hashCode()==hashcode&&(key==pNode.key || key.equals(pNode.key))){V value=pNode.value;tables[index]=pNode.next;//删除头节点return value;}//首先查找是否已经存在keywhile(pNode.next!=null){//当key的哈希值和真实值相等时if(pNode.next.key.hashCode()==hashcode&&(key==pNode.next.key || key.equals(pNode.next.key))){V value=pNode.value;pNode.next=pNode.next.next;//删除该节点return value;}pNode=pNode.next;//链表:遍历下一个值}return null;}public void print(){for(int i=0;i<tables.length;i++){MyEntry<K, V> entry=tables[i];if(entry!=null){while(entry!=null){System.out.println("key="+entry.key+",value="+entry.value);entry=entry.next;}}}}/** * 测试 * @param args */public static void main(String[] args) {MyHashMap<String, String> map=new MyHashMap<String, String>();for(int i=0;i<100;i++){map.put("stu"+i, ""+i);}for(int i=0;i<20;i++){map.remove("stu"+i);}map.print();}}

四.MyHashSet的实现

思路:和HashMap的思路一样,我们也是先计算hash值,然后折算出位置index,然后进行插入或删除。查找元素的效率很高,不需要对所有元素进行比较。

package edu.njupt.zhb;/* *@author: ZhengHaibo   *web:     http://blog.csdn.net/nuptboyzhb *mail:    zhb931706659@126.com *2014-3-25  Nanjing,njupt,China */public class MyHashSet<T>{/** * 节点 */public class MyEntry<T>{public T data;//当前数据public MyEntry<T> next;}private MyEntry<T> []tables;//数组private final int INIT_LEN=50;private int size;public MyHashSet(){tables=new MyEntry[INIT_LEN];size=0;}/** * 哈希函数构造方法 * @param hashcode 哈希值 * @return 对应的数组下标 */private int indexFor(int hashcode){return Math.abs(hashcode)%tables.length;}/** * 添加元素 * @param object * @return */public boolean add(T data){if(data==null){return false;}int hashcode=data.hashCode();//计算哈希值int index=indexFor(hashcode);//定位存储的位置MyEntry<T> head=tables[index];//取出该位置的第一个元素(链表的头)MyEntry<T> pNode=head;//查找链表中是否存在while(pNode!=null){if(pNode.data.hashCode()==hashcode&&(pNode.data==data||pNode.data.equals(data))){return false;//已经存在重复的元素}pNode=pNode.next;}//不存在重复的元素或该位置为空,将该节点添加到链表的头MyEntry<T> newNode=new MyEntry<T>();newNode.data=data;newNode.next=head;tables[index]=newNode;size++;return true;}public boolean remove(T data){if(data==null){return false;}int hashcode=data.hashCode();int index=indexFor(hashcode);MyEntry<T> head=tables[index];MyEntry<T> pNode=head;if(pNode.data.hashCode()==hashcode&&(pNode.data==data||pNode.data.equals(data))){tables[index]=pNode.next;size--;return true;}while(pNode.next!=null){if(pNode.next.data.hashCode()==hashcode&&(pNode.next.data==data||pNode.next.data.equals(data))){pNode.next=pNode.next.next;//删除该元素size--;return true;}pNode=pNode.next;}return false;}public void print(){for(int i=0;i<tables.length;i++){MyEntry<T> entry=tables[i];if(entry!=null){while(entry!=null){System.out.println(entry.data);entry=entry.next;}}}}/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubMyHashSet<String> set=new MyHashSet<String>();for(int i=0;i<100;i++){set.add(i+"");}for(int i=0;i<10;i++){set.remove(i+"");}System.out.println(set.size);set.print();}}


附录:

用链地址法处理冲突时的Hash表结构示意图如下:



相关博客:

[1].HashMap的实现原理:http://zhangshixi.iteye.com/blog/672697

[2].LinkedHashMap的实现原理: http://zhangshixi.iteye.com/blog/673789

4 0
原创粉丝点击