其实对于Hash表我感觉自己了解的还不是很深,自己写了一个Hash表,但还是参阅了很多的资料,下面谈谈我对Hash表的理解
1.Hash表的概念:
Hash表又称散列表,是除顺序表存储结构,链表存储结构和索引表存储结构之外的又一种存储线性表的存储结构
Hash表存储的基本思路:通过一个Hash函数h(Ki),把Ki映射为内存单元的地址(或称下标)h(Ki)
2.Hash函数的构造方法:
1).直接定址法:以关键字本身或关键字加上某个数值常量作为哈希地址
2).除留余数法:用关键字除以某个不大于Hash表长度的数所得的余数作为哈希地址
3).数字分析法:提取关键字中取值比较均匀的数字位作为哈希地址的方法(此法适用于所有关键字值都已知的情况,并需对关键字中每一位的取值分布情况进行分析)
4).还有平方取中法和折叠法等(这两种方法我都只是看到过,具体的不怎么了解)
3.哈希冲突:
1).产生原因:在Hash表存储结构中,很难避免一种"同义词冲突"的现象,即对于两个关键字Ki和Kj(i!=j),有Ki!=Kj(i!=j),但h(Ki)=h(Kj)
2).解决解决思路:当发送冲突时,通过Hash冲突函数(设为h(K))产生一个新的哈希地址使h(Ki)!=h(Kj),以此直到不存在hash冲突为止
3).解决方法:
1.拉链法----拉出一个动态链表代替静态顺序存储结构,可以避免哈希函数的冲突,不过缺点就是链表的设计过于麻烦,增加了编程复杂度。此法可以完全避免哈希函数的冲
突。
2.多哈希法-----设计二种甚至多种哈希函数,可以避免冲突,但是冲突几率还是有的,函数设计的越好或越多都可以将几率降到最低
3.开放地址法-----开放地址法有一个公式:Hi=(H(key)+di) MOD m i=1,2,...,k(k<=m-1) 其中,m为哈希表的表长。di 是产生冲突的时候的增量序列。如果di值可能为
1,2,3,...m-1,称线性探测再散列。 如果di取1,则每次冲突之后,向后移动1个位置.如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k
(k<=m/2) 称二次探测再散列。如果di取值可能为伪随机数列。称伪随机探测再散列。
4.建域法------假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表,另外设立存储空间向量OverTable[0..v]用以存储发生冲突的记录。
4.Hash表的实现:
这个程序是在借鉴了网上的一篇文章后重写的,算不得原创程序,我改了后输出结果还是有问题。
public class MyMap<K, V> {
private int size;// 当前容量
private static int INIT_CAPACITY = 11;// 默认容量
private ReHash<K, V>[] container;// 实际存储数据的数组对象
private static float LOAD_FACTOR = 0.75f;// 装载因子
private int max;// 能存的最大的数=capacity*factor
// 存放数据的数组
private ReHash[] nodeArray;
/**
* 主函数
* @param args
*/
public static void main(String[] args) {
MyMap<String, String> mm = new MyMap();
Long aBeginTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
mm.put("" + i, "" + i * 100);
}
Long aEndTime = System.currentTimeMillis();
System.out.println("插入数据的时间:" + (aEndTime - aBeginTime));
Long lBeginTime = System.currentTimeMillis();
// mm.get("100000");
String s = (String) mm.get("1000000");
Long lEndTime = System.currentTimeMillis();
System.out.println("查找时间" + (lEndTime - lBeginTime)+"查到的数据:"+s);
}
// 构造器,容量和装载因子
public MyMap(int init_Capaticy, float load_factor) {
if (init_Capaticy < 0)throw new IllegalArgumentException("Illegal initial capacity: " +init_Capaticy);
if (load_factor <= 0 || Float.isNaN(load_factor))throw new IllegalArgumentException("Illegal load factor: "+ load_factor);
this.LOAD_FACTOR = load_factor;
max = (int) (init_Capaticy * load_factor);
container = new ReHash[init_Capaticy];
}
// 使用默认参数的构造器
public MyMap() {
this(INIT_CAPACITY, LOAD_FACTOR);
}
/**
* 扩容
*
* @param newSize
*
*/
private void reSize(int newSize) {
// 1.声明新数组
ReHash<K, V>[] newTable = new ReHash[newSize];
max = (int) (newSize * LOAD_FACTOR);
// 2.复制已有元素,即遍历所有元素,每个元素再存一遍
for (int j = 0; j < container.length; j++) {
ReHash<K, V> entry = container[j];
while (null != entry) {
// setEntry(entry, newTable);
entry = entry.next;
}
}
// 3.改变指向
container = newTable;
}
/**
* 存储数据
* @param k
* @param v
* @return
*/
public boolean put(K k, V v) {
// 调用hashCode()方法来计算hash值
int hash = k.hashCode();
// 将所有信息封装为一个ReHash
ReHash<K, V> temp = new ReHash(k, v, hash);
if (setEntry(temp, container)) {
size++;
return true;
}
return false;
}
/**
* 将指定的结点temp添加到指定的hash表table当中 添加时判断该结点是否已经存在 如果已经存在,返回false 添加成功返回true
*
* @param temp
* @param table
* @return
*/
private boolean setEntry(ReHash<K, V> temp, ReHash[] table) {
// 根据hash值找到下标
int index = indexFor(temp.hash, table.length);
// 根据下标找到对应元素
ReHash<K, V> entry = table[index];
// 若存在
if (null != entry) {
// 遍历整个链表,判断是否相等
while (null != entry) {
// 判断相等的条件时应该注意,除了比较地址相同外,引用传递的相等用equals()方法比较
// 相等则不存,返回false
if ((temp.k == entry.k || temp.k.equals(entry.k))
&& temp.hash == entry.hash
&& (temp.v == entry.v || temp.v
.equals(entry.v))) {
return false;
}
// 不相等则比较下一个元素
else if (temp.k != entry.k && temp.v != entry.v) {
// 到达队尾,中断循环
if (null == entry.next) {
break;
}
// 没有到达队尾,继续遍历下一个元素
entry = entry.next;
}
}
// 当遍历到了队尾,如果都没有相同的元素,则将该元素挂在队尾
if (size > max) {
reSize(container.length * 4);
}
entry.next = temp;
}
// 若不存在,直接设置初始化元素
if (size > max) {
reSize(table.length * 4);
}
table[index] = temp;
temp.next = null;
return true;
}
/**
*查找value值
* @param k
* @return
*/
public V get(K k) {
ReHash<K, V> entry ;
// 计算K的hash值
int hash = k.hashCode();
//根据hash值找到下标
int index = indexFor(hash, container.length);
// 根据index找到链表
entry = container[index];
//若链表为空,返回null
if (null == entry) {
return null;
}
V result = null;
// 若不为空,遍历链表
while (null != entry) {
if (k == entry.k || entry.k.equals(k)) {
// return entry.v;
result = (V)entry.v;
break;
}
entry = entry.next;
}
return result;
}
/**
* 根据hash码,容器数组的长度,计算该哈希码在容器数组中的下标值
*
* @param hashcode
* @param containerLength
* @return
*/
public int indexFor(int hashcode, int containerLength) {
return hashcode & (containerLength - 1);
}
/**
* 存放键值对
* @author Administrator
*
* @param <K>
* @param <V>
*/
class ReHash<K, V> {
ReHash<K, V> next;// 下一个结点
K k;// key
V v;// value
int hash;// key对应的hash码
// 构造方法
ReHash(K k, V v, int hash) {
this.k = k;
this.v = v;
this.hash = hash;
}
}
}
输出的结果:
插入数据的时间:1265
查找时间0 查到的数据:null
输出结果还是有问题,查找的数据为null。