开放地址法实现HashTable
前注:本文不是讲解Java类库的Hashtable实现原理,而是根据计算机哈希表原理自己实现的一个Hashtable。
HashTable内部是用数组存放一个(Key-Value pair)键值对的引用,其原理是根据Key的hashCode来计算出数组下标。因为存放位置是直接计算出来,不需要遍历数据结构,这使得hash table具有快速增删查改的优势。下面介绍HashTable的实现步骤:
- 取得Key的hashCode。
通过Eclipse等工具可以轻松的复写Object的hashCode方法,生成的hashCode是不会有重复值的。
- 将hashCode与地址映射(压缩地址):
但是,Hashtable内部是用一个数组来存储Key-Value对的,数组容量有限,本文中设置数组容量为10。那么现在的问题就是如何将hashCode和数组地址相对应?一个简单的方法就是对hashCode取模运算(hashCode%10)。这样就可以将hashCode与数组地址做映射(多对一)。同时注意,hashCode可能为负数,而数组的下标不能为负数,所以在映射的时候需要处理一下。
int hashCode = Math.abs(key.hashCode())% this.maxSize;
- 解决hashCode重复问题:
上文已经说过,数组容量为10,当我们在将hashCode映射为数组下标的时候,肯定会遇到有重复的情况。比如hashCode为21和31,分别对他们取模运算,结果都是1,也就是说这两个hashCode对应数组的下标都是1.那么第一个元素添加上去后,再添加第二个元素,则新元素会覆盖之前的元素。这时,如果我们想后面添加的重复地址元素也能添加上去,只能将其放在其它位置。这时,我们可以将新元素放在改地址的下一位,如果下一位已经有元素,那么就继续往后找,直到找到空位为止(其实这个过程有些边界条件需要考虑,比如找到数组末尾后应跳到数组开头继续找。以及数组已经满了,找遍数组都找不到合适的位置,就应该提示该数组已满,不能插入)。
现在既然解决了上面的问题,也就是说每个元素都能在数组中找到合适的位置(除非数组已满)。那么可以根据这个算法实现数组的增删查改。下面是Hashtable的实现代码:
package org.lyk.impl; public class HashTable<K, V>
{
/**
* Key-Value pair 存放键值对
* @author liuyuank
*
*/
private class KeyValue
{
K key;
V value; private KeyValue(K key, V value)
{
this.key = key;
this.value = value;
} public K getKey()
{
return key;
} public void setKey(K key)
{
this.key = key;
} public V getValue()
{
return value;
} public void setValue(V value)
{
this.value = value;
} } private Object[] table;
private int maxSize = 10;
private int currentAmmount = 0; public HashTable()
{
this.table = new Object[this.maxSize];
} public HashTable(int maxSize) throws Exception
{
if (0 == maxSize || maxSize < 0 || maxSize > 100)
{
throw new Exception("table容量非法!");
} this.maxSize = maxSize;
this.table = new Info[maxSize];
} /**
* 增加一个键值对
* @param key
* @param value
*/
public void add(K key, V value)
{
//将hashCode映射到数组下标
int hashCode = Math.abs(key.hashCode())% this.maxSize; //将元素插入到数组中,如果该位置已经被占用,则循环查找下一个位置,直到找到合适的位置,或发现数组已满,退出循环
while (this.table[hashCode] != null
&& (this.currentAmmount < this.maxSize))
{
hashCode++;
hashCode = hashCode % this.maxSize;
} if (this.currentAmmount == this.maxSize)
{
//数组已满
System.out.println("Hash table 已满");
} else
{
//找到合适位置
this.table[hashCode] = new KeyValue(key, value);
this.currentAmmount++;
}
} /**
* 与add方法同样的算法,根据key值找到数组中元素,然后将改元素设置为null
* @param key
* @return
*/
public boolean remove(K key)
{
int hashCode = Math.abs(key.hashCode()) % this.maxSize;
int count = 0;
while (this.table[hashCode] != null && count < this.maxSize)
{
if (((KeyValue) this.table[hashCode]).getKey().equals(key))
{
this.table[hashCode] = null;
return true;
}
count++;
hashCode++;
hashCode = hashCode%this.maxSize;
} return false;
} public V get(K key)
{
int hashCode = Math.abs(key.hashCode()) % this.maxSize;
int count = 0;
while (this.table[hashCode] != null && count < this.maxSize)
{
if (key.equals(((KeyValue)this.table[hashCode]).getKey()))
return ((KeyValue) this.table[hashCode]).getValue(); hashCode++;
count++;
hashCode = hashCode%this.maxSize;
}
return null;
} public boolean contains(K key)
{
if (this.get(key) != null)
{
return true;
} else
{
return false;
}
} public void replace(K key, V value)
{
KeyValue kv = this.find(key);
if(kv != null)
{
kv.setValue(value);
}
} private KeyValue find(K key)
{
int hashCode = Math.abs(key.hashCode()) % this.maxSize;
int count = 0;
while (this.table[hashCode] != null && count < this.maxSize)
{
if (key.equals(((KeyValue)this.table[hashCode]).getKey()))
return ((KeyValue) this.table[hashCode]); hashCode++;
count++;
hashCode = hashCode%this.maxSize;
}
return null;
}
}
HashTable实现
package org.lyk.impl; import java.math.BigInteger; public class Info
{
private String name;
private String address;
private Integer age; public Info(String name, String address, Integer age)
{
super();
this.name = name;
this.address = address;
this.age = age;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
} // @Override
// public int hashCode()
// {
// final int prime = 27;
// int result = 1;
// result = prime*result + (this.name == null ? 0:this.name.hashCode());
// result = prime*result + (this.address == null ? 0:this.address.hashCode());
// result = prime*result + (this.age == null ? 0 : this.age.hashCode());
// return result;
// } @Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Info other = (Info) obj;
if (address == null)
{
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (age == null)
{
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null)
{
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getAddress()
{
return address;
}
public void setAddress(String address)
{
this.address = address;
}
public Integer getAge()
{
return age;
}
public void setAge(Integer age)
{
this.age = age;
}
@Override
public String toString()
{
return "Info [name=" + name + ", address=" + address + ", age=" + age
+ "]";
} }
Value实现
测试代码:String为Key Info为Value
package org.lyk.main; import org.lyk.impl.BiTree;
import org.lyk.impl.HashTable;
import org.lyk.impl.Info; public class Main
{
public static void main(String[] args)
{
HashTable<String, Info> ht = new HashTable<>();
for(int i =0; i <15;i++)
{
Info info = new Info("sheldon" + i, "address" + i , i);
//System.out.println("hashCode in main:" + info.getName().hashCode());
ht.add(info.getName(), info);
} String key = "sheldon3";
System.out.println(ht.contains(key));
ht.replace(key, new Info("谢耳朵","美国洛杉矶", 999));
System.out.println(ht.contains(key));
System.out.println(ht.get(key));
System.out.println("///~ main done");
} }
开放地址法实现HashTable的更多相关文章
- 面试准备 - HashTable 的C#实现 开放地址法
Hashtable是很经常在面试中遇到的数据结构,因为他的O(1)操作时间和O(n)空间 之所以自己写一份是因为: 加深对于hashtable的理解 某些公司面试的时候需要coding....... ...
- Hash冲突的线性探测开放地址法
在实际应用中,无论如何构造哈希函数,冲突是无法完全避免的. 开放地址法 这个方法的基本思想是:当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止.这个过程可用下式描述: ...
- Java解决Hash(散列)冲突的四种方法--开放地址法(线性探测,二次探测,伪随机探测)、链地址法、再哈希、建立公共溢出区
最近时间有点紧,暂时先放参考链接了,待有时间在总结一下: 查了好多,这几篇博客写的真心好,互有优缺点,大家一个一个看就会明白了: 参考 1. 先看这个明白拉链法(链地址法),这个带源码,很好看懂,只不 ...
- 开放地址法散列表ADT
数据结构定义如下: typedef unsigned int Index; typedef Index Position; struct HashTbl; typedef struct HashTbl ...
- 链地址法实现HashMap
前注:本文介绍的HashMap并非Java类库的实现.而是根据哈希表知识的一个实现. 上文介绍了开放地址法实现HashTable,它的缺点是对hashCode映射为地址后如果出现重复地址,则会占用其他 ...
- C# Dictionary源码剖析---哈希处理冲突的方法有:开放定址法、再哈希法、链地址法、建立一个公共溢出区等
C# Dictionary源码剖析 参考:https://blog.csdn.net/exiaojiu/article/details/51252515 http://www.cnblogs.com/ ...
- java 解决Hash(散列)冲突的四种方法--开放定址法(线性探测,二次探测,伪随机探测)、链地址法、再哈希、建立公共溢出区
java 解决Hash(散列)冲突的四种方法--开放定址法(线性探测,二次探测,伪随机探测).链地址法.再哈希.建立公共溢出区 标签: hashmaphashmap冲突解决冲突的方法冲突 2016-0 ...
- Python与数据结构[4] -> 散列表[2] -> 开放定址法与再散列的 Python 实现
开放定址散列法和再散列 目录 开放定址法 再散列 代码实现 1 开放定址散列法 前面利用分离链接法解决了散列表插入冲突的问题,而除了分离链接法外,还可以使用开放定址法来解决散列表的冲突问题. 开放定 ...
- 开放定址法——线性探测(Linear Probing)
之前我们所采用的那种方法,也被称之为封闭定址法.每个桶单元里存的都是那些与这个桶地址比如K相冲突的词条.也就是说每个词条应该属于哪个桶所对应的列表,都是在事先已经注定的.经过一个确定的哈希函数,这些绿 ...
随机推荐
- [安卓]windows下如何安装Android源码
本文改写于:http://www.cnblogs.com/skyme/archive/2011/05/14/2046040.html 1.下载并安装git: 在git-scm.com上下载并安装git ...
- iOS 注释的5要3不要和编码规范的26个方面
注释 代码注释,可以说是比代码本身更重要.这里有一些方法可以确保你写在代码中的注释是友好的: 不要重复阅读者已经知道的内容 能明确说明代码是做什么的注释对我们是没有帮助的. // If the col ...
- web前端基础篇⑥
LESS.①是一种拓展技术,基于css.②包含变量.混合.函数.运算.③简化css代码.降低维护成本④目前用的解析器(koala) 变量(值可变)@变量名:值步骤:①建立文件夹②建html和less两 ...
- java中Collection类及其子类
1:对象数组(掌握) (1)数组既可以存储基本数据类型,也可以存储引用类型.它存储引用类型的时候的数组就叫对象数组. 2:集合(Collection)(掌握) (1)集合的由来? 我们学习的是Java ...
- C#中的IntPtr类型
本文转自:http://zhidao.baidu.com/question/22825956.html 问: c#中无法将类型“int”隐式转换为“System.IntPtr” 这个是我引用了一个ap ...
- 关于重定向urlrewriter.urlmapping
大家都知道web.config 中的 urlMappings怎么用 <system.web> <urlMappings enabled="true"> &l ...
- unity5.3.4之no android module loaded
参考http://www.cnblogs.com/shenggege/p/5165616.html 最近从unity5.1.3升级到5.3.4的时候,发现有个问题: system.io.file' d ...
- 简介C#读取XML的两种方式
简介C#读取XML的两种方式 作者: 字体:[增加 减小] 类型:转载 时间:2013-03-03 在程序中访问进而操作XML文件一般有两种模型,分别是使用DOM(文档对象模型)和流模型,使用DOM的 ...
- WCF终结点配置
错误信息:已有针对 IP 终结点 127.0.0.1:8235 的侦听器.如果有其他应用程序已在侦听此终结点,或者,如果在服务主机中具有多个服务终结点,这些终结点具有相同的 IP 终结点但绑定配置不兼 ...
- JSP 数据库连接类 MySql数据库
数据库连接类的主要功能是连接数据库并且获得连接对象,以及关闭数据库.通过创建该类的实例,调用其中的方法,以避免重复操作. package chapter13; import java.sql.*; p ...