哈希表

哈希表也叫散列表,哈希表是一种数据结构,它提供了快速的插入操作和查找操作,无论哈希表总中有多少条数据,插入和查找的时间复杂度都是为O(1),因为哈希表的查找速度非常快,所以在很多程序中都有使用哈希表,例如拼音检查器。

哈希表也有自己的缺点,哈希表是基于数组的,我们知道数组创建后扩容成本比较高,所以当哈希表被填满时,性能下降的比较严重。

哈希表采用的是一种转换思想,其中一个中要的概念是如何将「键」或者「关键字」转换成数组下标?在哈希表中,这个过程有哈希函数来完成,但是并不是每个「键」或者「关键字」都需要通过哈希函数来将其转换成数组下标,有些「键」或者「关键字」可以直接作为数组的下标。我们先来通过一个例子来理解这句话。

java 哈希表添加多个数据 java哈希表实现_散列表

使用数组+链表的形式存放数据

java 哈希表添加多个数据 java哈希表实现_System_02


实现目标: 能自动扩展数组长度,并将原本数据再次散列其中

哈希表对象

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