哈希表的概念
哈希表,即散列表,本质上是一个数组也是一个列表,每个数组的元素都是一个链表的初始节点。
为什么要哈希表
众所周知,如果没有搜索功能,我们要在手机通讯录的一大串联系人里找到我们的目标联系人十分困难,但是这些联系人在经过首字母分类后,我们只需要找到目标联系人名字的首字母,就可快速找到目标联系人。可以说,哈希表,就是一个经过分类得出的数组,就如这里所说的手机通讯录一样。
哈希函数
哈希函数,即散列函数,实际上就相当于取首字母这一操作,它完成了我们需要的分类任务,是所有数据元素被分类的一种规则,即一种特殊的映射。同样,我们也可以取名字的最后一个字母作为我们通讯录的分类依据,即我们所需要的哈希函数。有了这个函数我们就知道每个数组要被分到散列数组的哪一个位置,即下标。
哈希数组和链表
哈希数组的每一个位置就相当于通讯录里的任何一个字母的分类,如a字母类,而挂在数组这个位置上的就是一个链表,链表里的节点就如同a开头下的所有联系人,节点里的键值对就如同联系人的姓名和手机号码
键值对
键值对(entry)就是每个链表节点里储存的一对数据,索引为key,即为键,值为value。哈希表储存这种一对一对的数据。我们存入一个key和一个value,通过哈希表我们就可以拿key取到我们的value
//键类
class Entry{
public int key;
public String value;
}
//链表节点
class HashNode{
//下一个节点的地址
public HashNode next;
//储存的数据
public Entry data;
}
public class THashMap {
//创建散列数组
HashNode[] hashArray = new HashNode[100];
public void put(int key,String value) {
//计算数组下标
int Index = hash(key);
//创建新的键
Entry entry = new Entry();
entry.key = key;
entry.value = value;
//创建新的节点
HashNode newNode = new HashNode();
newNode.data = entry;
//将新节点放在原链表最前端
HashNode temp = hashArray[Index];
hashArray[Index] = newNode;
newNode.next = temp;
}
public String get(int key) {
//获取数组下标
int Index = hash(key);
HashNode temp = hashArray[Index];
while(temp.data.key != key) {
temp = temp.next;
}
return temp.data.value;
}
//返回数组下标
public int hash(int key) {
//这里散列函数为取余函数
return key%100;
}
public static void main(String[] args) {
THashMap h = new THashMap();
h.put(50, "5050505");
h.put(60, "606060");
System.out.println(h.get(50));
System.out.println(h.get(60));
}
}
哈希表的扩容
上面是哈希表基本功能的实现。但是现在有两个问题,一、通讯录有首字母分类是比较方便,但是如果存储的联系人特别多,每个首字母下面都有成百上千的联系人那怎么办呢。二、又或者几百个联系人全都是首字母a开头的怎么办呢
这个时候哈希表就要进行扩容操作。这里我们只完成问题一
设所有存在的映射数为count,即储存了count个键值对,数组的总长度为length(初始为12),我们规定一个扩容因子为0.75,并且规定当count / length大于等于0.75的时候需要扩容,只需要在每次添加元素时进行一个判断,若需要扩容则调用扩容函数
代码如下
//键类
class Entry{
public int key;
public String value;
}
//链表节点
class HashNode{
//下一个节点的地址
public HashNode next;
//储存的数据
public Entry data;
}
public class THashMap {
//扩容因子
double enlarge = 0.75;
//映射总数,散列数组长度
double count=0;
int length=12;
//创建散列数组
HashNode[] hashArray = new HashNode[length];
public void put(int key,String value) {
//映射总数加一
count++;
//判断是否达到扩容条件
if (count/length >= enlarge) {
reSize();
}
//计算数组下标
int Index = hash(key);
//创建新的键
Entry entry = new Entry();
entry.key = key;
entry.value = value;
//创建新的节点
HashNode newNode = new HashNode();
newNode.data = entry;
//将新节点放在原链表最前端
HashNode temp = hashArray[Index];
hashArray[Index] = newNode;
newNode.next = temp;
}
public String get(int key) {
//获取数组下标
int Index = hash(key);
HashNode temp = hashArray[Index];
while(temp.data.key != key) {
temp = temp.next;
}
return temp.data.value;
}
//返回数组下标
public int hash(int key) {
//这里散列函数为取余函数
return key% length;
}
//扩容
public void reSize() {
length = length * 2;
HashNode[] newHashArray = new HashNode[length];
//遍历原来的散列数组
for (int i=0; i<length/2; i++) {
if (hashArray[i] != null) {
//获取新的映射
int newIndex = hash(hashArray[i].data.key);
HashNode temp = newHashArray[newIndex];
newHashArray[newIndex] = hashArray[i];
newHashArray[newIndex].next = temp;
}
}
//再将散列数组地址指向新数组
hashArray = newHashArray;
}
public static void main(String[] args) {
THashMap hash = new THashMap();
hash.put(5, "Hello");
hash.put(32, " ");
hash.put(67, "World");
hash.put(98, "Hello");
hash.put(854, "Hello");
hash.put(745, "Hello");
hash.put(554, "Hello");
hash.put(224, "Hello");
hash.put(223, "Hello");
hash.put(257, "Hello");
hash.put(633, "Hello");
hash.put(951, "Hello");
hash.put(430, "Hello");
hash.put(764, "Hello");
System.out.println(hash.get(67));
}
}