加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 电商 > 正文

自己动手实现java数据结构(五)哈希表

发布时间:2021-04-04 12:21:30 所属栏目:电商 来源:网络整理
导读:1.哈希表介绍 前面我们已经介绍了许多类型的数据结构。在想要查询容器内特定元素时,有序向量使得我们能使用二分查找法进行精确的查询(( O(logN)对数复杂度,很高效 )。 可人类总是不知满足,依然在寻求一种更高效的特定元素查询的数据结构, 哈希表/散列表(

  2. 当迭代器的下标到达当前插槽链表的末尾时,迭代器下标需要跳转到靠后插槽的第一个非空数据节点。

     * 哈希表 迭代器实现
     class Itr implements Iterator<EntryNode<K,1)"> {
        
         * 迭代器 当前节点
         * */
         currentNode;

        
         * 迭代器 下一个节点
         *  nextNode;

        
         * 迭代器 当前内部数组的下标
         *  currentIndex;

        
         * 默认构造方法
         * private Itr(){
            :::如果当前哈希表为空,直接返回
            if(HashMap..isEmpty()){
                ;
            }
            :::在构造方法中,将迭代器下标移动到第一个有效的节点上

            :::遍历内部数组,找到第一个不为空的数组插槽slot
            int i=0; i<HashMap.:::设置当前index
                this.currentIndex = i;

                EntryNode<K,V> firstEntryNode = HashMap..elements[i];
                :::找到了第一个不为空的插槽slot
                if(firstEntryNode != :::nextNode = 当前插槽第一个节点
                    this.nextNode = firstEntryNode;

                    :::构造方法立即结束
                    ;
                }
            }
        }

        @Override
         hasNext() {
            this.nextNode != );
        }

        @Override
        public EntryNode<K,1)"> next() {
            this.currentNode = .nextNode;
            :::暂存需要返回的节点
            EntryNode<K,V> needReturn = .nextNode;

            :::nextNode指向自己的next
            this.nextNode = .nextNode.next;
            :::判断当前nextNode是否为null
            this.nextNode == :::说明当前所在的桶链表已经遍历完毕

                :::寻找下一个非空的插槽
                int i=this.currentIndex+1; i<HashMap.:::设置当前index
                     i;

                    EntryNode<K,1)">.elements[i];
                    :::找到了后续不为空的插槽slot
                    ){
                        :::nextNode = 当前插槽第一个节点
                         firstEntryNode;
                        :::跳出循环
                        break;
                    }
                }
            }
             needReturn;
        }

        @Override
         remove() {
            this.currentNode == throw new IteratorStateErrorException("迭代器状态异常: 可能在一次迭代中进行了多次remove操作");
            }

            :::获得需要被移除的节点的key
            K currentKey = .currentNode.key;
            :::将其从哈希表中移除
            HashMap..remove(currentKey);

            :::currentNode设置为null,防止反复调用remove方法
            ;
        }
    }

5.哈希表性能

5.1 空间效率:

  哈希表的空间效率很大程度上取决于负载因子。通常,为了保证哈希表查询的高效性,负载因子都设置的比较小(小于1),因而可能会出现许多空的插槽,浪费空间。

  总体而言,哈希表的空间效率低于向量和链表。

5.2 时间效率:

  一般的,哈希表增删改查接口的时间复杂度都是O(1)。但是出现较多的hash冲突时,冲突范围内的key的增删改查效率较低,时间效率会有一定的波动。

  总体而言,哈希表的时间效率高于向量和链表。

  哈希表的时间效率很高,可天下没有免费的午餐,据统计,哈希表的空间利用率通常情况下还不到50%。

  哈希表是一个使用空间来换取时间的数据结构,对查询性能有较高要求的场合,可以考虑使用哈希表。

6.哈希表总结

6.1 当前版本缺陷

  至此,我们已经实现了一个基础的哈希表,但还存在许多明显缺陷:   

  1.当hash冲突比较频繁时,查询效率急剧降低。

  jdk在1.8版本的哈希表实现(java.util.HashMap)中,对这一场景进行了优化。当内部桶链表的节点个数超过一定数量(默认为8)时,会将插槽中的桶链表转换成一个红黑树(查询效率为O(logN))。

  2.不支持多线程

  在多线程的环境,并发的访问一个哈希表会导致诸如:扩容时内部节点死循环、丢失插入数据等异常情况。

6.2 查询特定元素的方法

  我们目前查询特定元素有几种不同的方法:

  1.顺序查找

  在无序向量或者链表中,查找一个特定元素是通过从头到尾遍历容器内元素的方式实现的,执行速度正比于数据量的大小,顺序查找的时间复杂度为O(n),效率较低。

  2.二分查找

  在有序向量以及后面要介绍的二叉搜索树中,由于容器内部的元素是有序的,因此可以通过二分查找比较的方式查询特定的元素,二分查找的时间复杂度为O(logN),效率较高。 

  3.哈希查找

  在哈希表中,通过直接计算出数据hash值对应的插槽(slot)(时间复杂度O(1)),查找出对应的数据,哈希查找的时间复杂度为O(1),效率极高。

特定元素的查找方式和排序算法的关系

  1.顺序查找对应冒泡排序、选择排序等,效率较低,时间复杂度(O(n2))。

  2.二分查找对应快速排序、归并排序等,效率较高,时间复杂度(O(nLogn))。

  3.哈希查找对应基排序,效率极高,时间复杂度(O(n))。

  在大牛刘未鹏的博客中有更为详细的说明,http://mindhacks.cn/2008/06/13/why-is-quicksort-so-quick。

6.3 完整代码

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读