【数据结构】哈希表的原理及实现

简介: 【数据结构】哈希表的原理及实现

1.什么是哈希表

  • 哈希表又称为散列表,它是一种以键值对形式来存储数据的结构,只要输入待查找的key,就可以通过该key寻找到对应的值。对应函数:y = f(key)
  • 通过把关键码映射到表中的对应位置来访问对应信息,来加快查找速度
  • 哈希表用的是数据支持下标随机、访问数据的特性来实现的,所以说哈希表是数组的扩展,是由数组演化而成。
  • 数据储存 可以通过 数组+链表 的方式来实现
  • 哈希表示意图:
  • 88bd6512a89a4f44b8f6fdc00c5ee0c2.jpg

为什么要使用哈希表?

  • 我们知道,在使用数据进行数据查找是,是从头来进行查询的,这样即查找的方式即慢,又不方便。但使用哈希表就可以提高检索速度,减少资源的消耗。

2.哈希表功能实现

哈希表中增加数据 put(K key,V value)
根据key获取对应的value V get(K key)
删除指定key的元素 remove(K key)
输出哈希表中所有元素 show()

(1)哈希表初始化


ca2932973f6e42e2a652fcdab576a656.jpg

(2)新增哈希表中数据

fee5796bb5714724bc15e0734625512c.jpg

(3)修改哈希表中指定key 的value

651509acecd9489e8be62a561d760048.jpg

(4)删除哈希表中指定key的元素

14e65dab069e486abacf5c22c7bd98ef.jpg

(5)遍历输出哈希表中所有数据

0d9b3f088ab5446f9715dbb93fd26bab.jpg

3.哈希表代码实现

(1)整体代码实现

/**
 * 哈希表实现
 * @param <K>
 * @param <V>
 */
public class HashTable<K,V> {
    public static void main(String[] args) {
        HashTable<Integer,String> hashTable = new HashTable<>();
        hashTable.put(1,"李祥");
        hashTable.put(2,"张三");
        hashTable.put(3,"李四");
        System.out.println("新增三个元素 :李祥,张三,李四");
        System.out.println("打印当前哈希表:");
        hashTable.show();
        System.out.println("获取key为3的元素value:"+ hashTable.get(3));
        System.out.println("修改key为3的元素value为 小李 :");
        hashTable.put(3,"小李");
        System.out.println("打印当前哈希表:");
        hashTable.show();
        System.out.println("删除key为2的元素:");
        hashTable.remove(2);
        hashTable.show();
    }
    /**
     * 定义链表数组
     */
    private final LinkedList<K,V>[] linkedArr;
    /**
     * 默认数组长度
     */
    private final static int DEFAULT_LENGTH = 10;
    /**
     * 定义数组的容量
     */
    public int maxSize;
    /**
     * 构造方法初始化
     */
    public HashTable(){
        //定义数组的最大长度
        this.maxSize = DEFAULT_LENGTH;
        //初始化数组
        linkedArr = (LinkedList<K,V>[]) new LinkedList[maxSize];
        //对数组中的每一条链表都要初始化
        for(int i=0;i<maxSize;i++){
            linkedArr[i]=new LinkedList<>();
        }
    }
    /**
     * put 元素
     * @param key
     * @param value
     */
    public void put(K key,V value){
        //key取hashcode根据数组长度取余获取index
        int hashCode = key.hashCode();
        int index =(hashCode % maxSize);
        //把对应的课程 插入到对应的哈希表
        Node<K, V> oldNode = linkedArr[index].get(key);
        //如果当前key 不存在 则表示新增,如果key存在表示替换原有的value
        if(oldNode==null){
            //创建Node节点
            Node<K,V> node = new Node<>(key,value);
            linkedArr[index].add(node);
        }else{
            linkedArr[index].update(key,value);
        }
    }
    /**
     * 根据key值获取value
     * @param key
     * @return
     */
    public V get(K key){
        //获取数组的index
        int hashCode = key.hashCode();
        int index =(hashCode % maxSize);
        //链表中查找key对应的value
        Node<K, V> node = linkedArr[index].get(key);
        return node.value;
    }
    /**
     * 根据key值获取value
     * @param key
     * @return
     */
    public void remove(K key){
        //获取数组的index
        int hashCode = key.hashCode();
        int index =(hashCode % maxSize);
        //链表中查找key对应的value
        linkedArr[index].remove(key);
    }
    /**
     * 输出当前哈希表中的所有元素
     */
    public void show(){
        List<String> showStr = new ArrayList<>();
        for (int i = 0; i < linkedArr.length; i++) {
            List<Node<K, V>> list = linkedArr[i].list();
            for (Node<K, V> node : list) {
                String strNode = "{key="+node.key+",value="+node.value+"}";
                showStr.add(strNode);
            }
        }
        System.out.println("["+String.join(",",showStr)+"]");
    }
    /**
     * 链表存储数据
     * @param <K>
     * @param <V>
     */
    class LinkedList<K,V>{
        /**
         * 定义头节点
         */
        private Node<K,V> head;
        /**
         * 链表中增加数据
         * @param course
         */
        public void add(Node<K,V> data){
            //如果说 head==null  把第一个元素加进去
            if(head==null){
                head=data;
                return;
            }
            // 遍历添加
            Node<K,V> cur=head;
            while (cur.next != null) {
                //跳出循环的条件
                //找到最后一个节点
                cur = cur.next;
            }
            cur.next=data;
        }
        /**
         * 获取链表中全部数据
         */
        public List<Node<K,V>> list(){
            List<Node<K,V>> nodeList = new ArrayList<>();
            if(head==null){
                return nodeList;
            }
            //获取每一项加入到集合中
            Node<K,V> cur=head;
            while (true){
                nodeList.add(cur);
                if(cur.next==null){
                    break;
                }
                cur=cur.next;
            }
            return nodeList;
        }
        /**
         * 根据key 获取对应的node
         * @param key
         * @return
         */
        public Node<K,V> get(K key){
            if(head==null){
                return null;
            }
            //链表有课程 遍历输出 创建一个辅助指针
            Node<K,V> cur=head;
            while (!cur.key.equals(key)) {
                cur = cur.next;
            }
            return cur;
        }
        /**
         * 修改链表中的对应key的值
         * @param key
         * @param value
         */
        public void update(K key, V value) {
            //如果说 head==null,直接返回
            if(head==null){
                return;
            }
            //添加的不是第一门课程 遍历添加
            Node<K,V> cur=head;
            do {
                if(cur.key.equals(key)){
                    cur.value = value;
                    break;
                }
                cur = cur.next;
            }while(cur.next != null);
        }
        /**
         * 删除对应key的node
         * @param key
         * @return
         */
        public void remove(K key) {
            //如果说 head==null,直接返回
            if(head==null){
                return;
            }
            //遍历查找到对应key的node节点
            int x = 0;
            Node<K,V> cur = head;
            while(cur != null) {
                x++;
                if(cur.key.equals(key)){
                    break;
                }
                cur = cur.next;
            }
            //当 cur 为空或这cur.next为空时
            if(cur==null||cur.next==null){
                //判断寻找节点是不是只有一个元素
                if(x == 1){
                    //只有一个元素时,将头节点改成null
                    head = null;
                }else{
                    //当是最后一个元素时,找到当前节点的前一个元素,将前一个元素的next改成null
                    Node<K,V> prev = head;
                    while(prev.next!=null){
                        if (prev.next.key.equals(key)){
                            break;
                        }
                        prev = prev.next;
                    }
                    prev.next=null;
                }
                return;
            }
            cur.key = cur.next.key;
            cur.value = cur.next.value;
            cur.next = cur.next.next;
        }
    }
    class Node<K,V>{
        /**
         * 定义数据
         */
        public V value;
        /**
         * 定义key
         */
        public K key;
        /**
         * 定义下一个节点的
         */
        public Node<K,V> next;
        public Node(K key,V value) {
            this.value = value;
            this.key = key;
        }
    }
}

(2)代码测试

176af755dd144da396181ceaae704996.jpg

相关文章
|
2月前
|
算法 Java 数据库
数据结构与算法学习十五:哈希表
这篇文章详细介绍了哈希表的概念、应用实例、实现思路,并提供了使用Java实现的哈希表代码。
66 0
数据结构与算法学习十五:哈希表
|
6月前
|
存储 NoSQL Redis
Redis系列学习文章分享---第十六篇(Redis原理1篇--Redis数据结构-动态字符串,insert,Dict,ZipList,QuickList,SkipList,RedisObject)
Redis系列学习文章分享---第十六篇(Redis原理1篇--Redis数据结构-动态字符串,insert,Dict,ZipList,QuickList,SkipList,RedisObject)
85 1
|
6月前
|
存储 消息中间件 缓存
Redis系列学习文章分享---第十七篇(Redis原理篇--数据结构,网络模型)
Redis系列学习文章分享---第十七篇(Redis原理篇--数据结构,网络模型)
101 0
|
3月前
|
存储 Java Serverless
【数据结构】哈希表&二叉搜索树详解
本文详细介绍了二叉搜索树和哈希表这两种数据结构。二叉搜索树是一种特殊二叉树,具有左子树节点值小于根节点、右子树节点值大于根节点的特点,并且不允许键值重复。文章给出了插入、删除和搜索等方法的具体实现。哈希表则通过哈希函数将键名映射为数组下标,实现快速查找,其插入、删除和查找操作时间复杂度理想情况下为O(1)。文中还讨论了哈希函数的设计原则、哈希冲突的解决方法及哈希表的实现细节。
67 8
【数据结构】哈希表&二叉搜索树详解
|
3月前
|
设计模式 安全 Java
HashMap底层原理:数据结构+put()流程+2的n次方+死循环+数据覆盖问题
假如有T1、T2两个线程同时对某链表扩容,他们都标记头结点和第二个结点,此时T2阻塞,T1执行完扩容后链表结点顺序反过来,此时T2恢复运行再进行翻转就会产生环形链表,即B.next=A;采用2的指数进行扩容,是为了利用位运算,提高扩容运算的效率。JDK8中,HashMap采用尾插法,扩容时链表节点位置不会翻转,解决了扩容死循环问题,但是性能差了一点,因为要遍历链表再查到尾部。例如15(即2^4-1)的二进制为1111,31的二进制为11111,63的二进制为111111,127的二进制为1111111。
HashMap底层原理:数据结构+put()流程+2的n次方+死循环+数据覆盖问题
|
2月前
|
消息中间件 存储 Java
数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
40 4
|
2月前
|
存储 缓存 Java
【数据结构】哈希表
【数据结构】哈希表
53 1
|
2月前
|
搜索推荐 索引
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(二)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
|
2月前
|
搜索推荐 C++
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(一)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
|
2月前
|
Java C++
【数据结构】探索红黑树的奥秘:自平衡原理图解及与二叉查找树的比较
本文深入解析红黑树的自平衡原理,介绍其五大原则,并通过图解和代码示例展示其内部机制。同时,对比红黑树与二叉查找树的性能差异,帮助读者更好地理解这两种数据结构的特点和应用场景。
38 0