哈希表
哈希表也叫散列表,哈希表是一种数据结构,它提供了快速的插入操作和查找操作,无论哈希表总中有多少条数据,插入和查找的时间复杂度都是为O(1),因为哈希表的查找速度非常快,所以在很多程序中都有使用哈希表,例如拼音检查器。
哈希表也有自己的缺点,哈希表是基于数组的,我们知道数组创建后扩容成本比较高,所以当哈希表被填满时,性能下降的比较严重。
哈希表采用的是一种转换思想,其中一个中要的概念是如何将「键」或者「关键字」转换成数组下标?在哈希表中,这个过程有哈希函数来完成,但是并不是每个「键」或者「关键字」都需要通过哈希函数来将其转换成数组下标,有些「键」或者「关键字」可以直接作为数组的下标。我们先来通过一个例子来理解这句话。
使用数组+链表的形式存放数据
实现目标:
能自动扩展数组长度,并将原本数据再次散列其中
哈希表对象
package com.zl.hashTable;
public class HashTable<K, T> {
//核心数组
private ObjectLinkedList<K, T>[] map = new ObjectLinkedList[16];
//所有链表的节点数目,以后用于红黑树演变,现在没什么用
private int nodeQuantity = 0;
//数组元素占用数目
private int arrayQuantity = 0;
//当前数组数目大小上限
private int arraySizeMax = INITIAL_SIZE;
//初始大小
private static final int INITIAL_SIZE = 16;
//扩展倍数
private static final double EXPAND_MULTIPLE = 1.5;
//扩展阈值
private static final double THRESHOLD = 0.8;
//创建数组元素开头管理节点
private ObjectLinkedList<K,T> generateObjectLinkedList(K key,T data) {
ObjectLinkedList<K, T> linkedList = new ObjectLinkedList<>();
linkedList.put(key,data);
return linkedList;
}
//是否到达了扩展倍数
public boolean IsExpand() {
return arraySizeMax * THRESHOLD >= (double) arrayQuantity;
}
//扩展
private synchronized void expand(){
//防止多线程的一个判断
if (!IsExpand()){
return;
}
int length = (int)(this.arraySizeMax * HashTable.EXPAND_MULTIPLE);
this.arraySizeMax = length;
ObjectLinkedList<K,T>[] newMap = new ObjectLinkedList[length];
//把原本数组的开头管理节点数据按照一定规矩依次迁移
for (ObjectLinkedList<K, T> list : map) {
if(list!=null){
int index = (list.getNode().hashCode() * arraySizeMax) % arraySizeMax;
newMap[index] = list;
}
}
this.map = newMap;
System.gc();
}
//查找数据
public T get(K key){
int index = key.hashCode() / arraySizeMax % arraySizeMax;
if(map[index]==null){
return null;
}else{
return map[index].get(key);
}
}
//删除数据
public T remove(K key){
int index = key.hashCode() / arraySizeMax % arraySizeMax;
if(map[index]==null){
return null;
}else{
T remove = map[index].remove(key);
if(remove!=null){
nodeQuantity--;
}
return remove;
}
}
//添加数据
public void put(K key, T data) {
if(IsExpand()){
expand();
}
int index = key.hashCode() / arraySizeMax % arraySizeMax;
//判断初始管理节点是否有数据
if(map[index]!=null && map[index].isEmpty()){
map[index].put(key,data);
}else if(map[index]==null){
ObjectLinkedList<K, T> ktObjectLinkedList = generateObjectLinkedList(key, data);
map[index] = ktObjectLinkedList;
arrayQuantity++;
}else{
map[index].put(key,data);
}
nodeQuantity++;
}
public static void main(String[] args) {
HashTable<String, String> hashTable = new HashTable<>();
System.out.println(hashTable.map[0]);
}
}
/**
* 头节点与管理的类
* 没有头节点概念,直接存放元素
*/
class ObjectLinkedList<K, T> {
//元素第一个节点,双向链表
private ObjectNode<K, T> node = null;
//链表尾部
private ObjectNode<K, T> nodeEnd = null;
public int getElementQuantity() {
return elementQuantity;
}
//元素数量
private int elementQuantity = 0;
//判断元素节点是否为空
public boolean isEmpty() {
return elementQuantity == 0;
}
//查找元素节点
public T get(K key) {
if (isEmpty()) {
return null;
}
ObjectNode<K, T> temp = this.node;
while (temp != null) {
if (temp.getKey().equals(key)) {
//找到了
return temp.getData();
}
temp = temp.getNext();
}
return null;
}
//添加元素节点
public synchronized void put(K key, T data) {
if (isEmpty()) {
//是空的
this.node = new ObjectNode<>(key, data);
this.node.setPre(this.node);
nodeEnd = node;
elementQuantity++;
return;
}
//判断是否key重合
ObjectNode<K, T> temp = this.node;
while (temp != null) {
if (temp.getKey().equals(key)) {
//找到了,有重合,覆盖数据
temp.setData(data);
return;
}
temp = temp.getNext();
}
//没有重合,至链表尾部添加
ObjectNode<K, T> node = new ObjectNode<>(key, data);
node.setPre(nodeEnd);
nodeEnd.setNext(node);
nodeEnd = node;
elementQuantity++;
}
//删除元素节点
public synchronized T remove(K key) {
if(elementQuantity==1){
T data = this.node.getData();
this.node = null;
nodeEnd = null;
elementQuantity = 0;
return data;
}
ObjectNode<K, T> temp = this.node;
while (temp != null) {
if (temp.getKey().equals(key)) {
if(temp == nodeEnd){
T data = temp.getData();
temp.getPre().setNext(null);
nodeEnd = temp.getPre();
return data;
}
//找到了,删除
ObjectNode<K, T> pre = temp.getPre();
ObjectNode<K, T> next = temp.getNext();
if(pre!=null)
pre.setNext(next);
if(next!=null)
next.setPre(pre);
elementQuantity--;
return temp.getData();
}
temp = temp.getNext();
}
return null;
}
public ObjectNode<K, T> getNode() {
return node;
}
public void setNode(ObjectNode<K, T> node) {
this.node = node;
}
public ObjectNode<K, T> getNodeEnd() {
return nodeEnd;
}
public void setNodeEnd(ObjectNode<K, T> nodeEnd) {
this.nodeEnd = nodeEnd;
}
}
/**
* 节点类,双向链表类型
*/
class ObjectNode<K, T> {
private K key;
private ObjectNode<K, T> pre;
private ObjectNode<K, T> next;
private T data;
public ObjectNode(K key, T data) {
this.key = key;
this.data = data;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public ObjectNode<K, T> getPre() {
return pre;
}
public void setPre(ObjectNode<K, T> pre) {
this.pre = pre;
}
public ObjectNode<K, T> getNext() {
return next;
}
public void setNext(ObjectNode<K, T> next) {
this.next = next;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
员工对象
package com.zl.hashTable;
public class Emp {
private int id;
private String name;
private int age;
private String sex;
public Emp(int id, String name, int age, String sex) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
}
public Emp() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
测试代码
package com.zl.hashTable;
public class Test {
public static void main(String[] args) {
HashTable<String,Emp> ht = new HashTable<>();
ht.put("1",new Emp(1,"张某",18,"男"));
ht.put("3",new Emp(3,"朱某",19,"男"));
ht.put("2",new Emp(2,"李某",20,"男"));
System.out.println("查找123编号=================");
for (int i = 1; i < 4; i++) {
Emp emp = ht.get(i + "");
System.out.println(emp);
}
System.out.println("删除编号2================");
Emp remove = ht.remove("2");
System.out.println(remove);
System.out.println("查找123编号=================");
for (int i = 1; i < 4; i++) {
Emp emp = ht.get(i + "");
System.out.println(emp);
}
System.out.println("修改编号1================");
ht.put("1",new Emp(233,"张笨蛋",1,"男"));
System.out.println("查找123编号=================");
for (int i = 1; i < 4; i++) {
Emp emp = ht.get(i + "");
System.out.println(emp);
}
}
}
查找123编号=================
Emp{id=1, name='张某', age=18, sex='男'}
Emp{id=2, name='李某', age=20, sex='男'}
Emp{id=3, name='朱某', age=19, sex='男'}
删除编号2================
Emp{id=2, name='李某', age=20, sex='男'}
查找123编号=================
Emp{id=1, name='张某', age=18, sex='男'}
null
Emp{id=3, name='朱某', age=19, sex='男'}
修改编号1================
查找123编号=================
Emp{id=233, name='张笨蛋', age=1, sex='男'}
null
Emp{id=3, name='朱某', age=19, sex='男'}
进程已结束,退出代码为 0