Java数据结构——哈希表

什么是哈希表?
哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方。

哈希表充分体现了算法设计领域的经典思想:空间换时间。哈希表是时间和空间之间的平衡。其中的哈希函数是最重要的,“键”通过哈希函数得到的“索引”分布越均匀越好。但是哈希表会失去顺序性。

哈希函数的设计
对于整型

  1. 小范围正整数直接使用
  2. 小范围负整数进行偏移 -100~100 ----> 0~200
  3. 大整数:模一个素数

对于浮点型
转成整型处理


对于字符串
也是转成整型处理

int hash=0forint i=0;i<s.length();i++
{
hash=(hash*B+s.charAt(i))%M;
}

对于复合类型
依然转成整型处理

hash(code)=((((c%M)*B+o)%M*B+d)%M*B+e)%M; 

但是,转成整型处理,并不是唯一的方法

哈希函数设计原则

  1. 一致性:如果a==b,则hash(a)==hash(b),反之不一定成立
  2. 高效性:计算高效简便
  3. 均匀性:哈希值分布均匀

哈希冲突的处理
链地址法(O(1))

在jdk7之前是数组+链表
jdk8开始,当链表长度超过一定值后会转换成红黑树

开放地址法
不会形成链,当遇到哈希冲突时,直接往冲突的位置的后面的第一个空的位置里放。当这个哈希表足 够大且元素存的足够多的时候,会导致查找下一个空位的效率变得很低。

 

实现一个自己的哈希表

import java.util.TreeMap;

public class HashTable<K, V> {
private TreeMap<K, V>[] hashtable;
private int M;
private static final int upperTol = 10;
private static final int lowerTol = 2;
private static final int initCapacity = 7;
private final int[] capacity = { 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613,
393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
805306457, 1610612741 };
private int capacityIndex = 0;
private int size;

public HashTable() {
this.M = capacity[capacityIndex];
size = 0;
hashtable = new TreeMap[M];
for (int i = 0; i < M; i++) {
hashtable[i] = new TreeMap<>();
}
}

private int hash(K Key) {
return (Key.hashCode() & 0x7fffffff) % M;
}

public int getSize() {
return size;
}

public void add(K key, V value) {
TreeMap<K, V> map = hashtable[hash(key)];
if (map.containsKey(key)) {
map.put(key, value);
} else {
map.put(key, value);
size++;
if (size >= upperTol * M && capacityIndex + 1 < capacity.length) {
capacityIndex++;
resize(capacity[capacityIndex]);
}
}
}

public V remove(K key) {
TreeMap<K, V> map = hashtable[hash(key)];
V ret = null;
if (map.containsKey(key)) {
ret = map.remove(key);
size--;
if (size < lowerTol * M && capacityIndex - 1 >= 0) {
capacityIndex--;
resize(capacity[capacityIndex]);
}
}
return ret;
}

public void set(K key, V value) {
TreeMap<K, V> map = hashtable[hash(key)];
if (!map.containsKey(key)) {
throw new IllegalArgumentException("don't find");
} else {
map.put(key, value);
}
}

public boolean contain(K key) {
return hashtable[hash(key)].containsKey(key);
}

public V get(K key) {
TreeMap<K, V> map = hashtable[hash(key)];
return map.get(key);
}

private void resize(int newM) {
TreeMap<K, V>[] newTable = new TreeMap[newM];
for (int i = 0; i < newM; i++) {
newTable[i] = new TreeMap<>();
}
int oldM = M;
this.M = newM;
for (int i = 0; i < oldM; i++) {
TreeMap<K, V> map = hashtable[i];
for (K key : map.keySet()) {
newTable[hash(key)].put(key, map.get(key));
}
}
this.hashtable = newTable;
}
}

 

posted @ 2019-05-01 19:33  橘子洲头。  阅读(204)  评论(0编辑  收藏  举报