java 中的 HashMap 用到的数据结构:
我们先说二叉搜索树,再说平衡二叉树,最后红黑树。
又称之为二叉排序树(二叉查找树),它或许是一棵空树,或许是具有以下性质的二叉树:
二叉搜索树的这种特性,使得我们在此二叉树上查找某个值就很方便了,从根节点开始,若要寻找的值小于根节点的值,则在左子树上去找,反之则去右子树查找,知道找到与值相同的节点。插入节点也是一样的道理,从根节点出发,所要插入的值,若小于根节点则去左子树寻找该节点所对应的位置,反之去右子树寻找,直到找到该节点合适的位置。
二叉搜索树的特性便于我们进行查找插入删除等一系列操作,其时间复杂度为O(logn)
,但是,如果遇见最差的情况,比如以下这棵树:
这棵树,说是树,其实它已经退化成链表了,但从概念上来看,它仍是一棵二叉搜索树,只要我们按照逐次增大,如1、2、3、4、5、6的顺序构造一棵二叉搜索树,则形如上图。那么插入的时间复杂度就变成了
O(n)
,导致这种糟糕的情况原因是因为这棵树极其不平衡,右树的重量远大于左树,因此我们提出了叫平衡二叉搜索树的结构,又称之为 AVL 树,是因为平衡二叉搜索树的发明者为 Adel’son-Vel’skii 和Landis 二人。
它能保持二叉树的高度平衡,尽量降低二叉树的高度,减少树的平均查找长度。
AVL树的性质:
解决了树不平衡的问题,为什么还要红黑树呢?
创建一颗平衡二叉树的成本其实不小,为了让它重新维持在一个平衡状态,就需要对其进行旋转处理。这个时候就有人开始思考,并且提出了红黑树的理论,那么红黑树到底比AVL树好在哪里?
红黑树与AVL树的比较:
红黑树的性质:
红黑树是一棵二叉搜索树,它在每个节点增加了一个存储位记录节点的颜色,可以是 RED ,也可以是 BLACK ;通过任意一条从根到叶子简单路径上颜色的约束,红黑树保证最长路径不超过最短路径的二倍,因而近似平衡。
具体性质如下:
那么为什么当满足以上性质时,就能保证最长路径不超过最短路径的二倍了呢?我们分析一下:
最短路径就是全黑节点,最长路径就是一个红节点一个黑节点,最后黑色节点相同时,最长路径刚好是最短路径的两倍
红黑树插入节点过程大致分析:
RBTree 为二叉搜索树,我们按照二叉搜索树的方法对其进行节点插入
RBTree 有颜色约束性质,因此我们在插入新节点之后要进行颜色调整
具体步骤如下:
什么是散列?
Hash(哈希),又称“散列”,通过计算哈希值,打破元素之间原有的关系,使集合中的元素按照散列函数的分类进行排列。
计算 hashCode 的过程就称作 哈希,在某种程度上,散列是与排序相反的一种操作,排序是将集合中的元素按照某种方式比如字典顺序排列在一起。
在介绍一些集合时,我们总强调需要重写某个类的 equlas() 方法和 hashCode() 方法,确保唯一性。这里的 hashCode() 表示的是对当前对象的唯一标示。
为什么要用到散列?
通过 哈希 计算,可以大大减少比较次数,使用数组或者链表来存储元素,一旦存储的内容数量特别多,需要占用很大的空间,而且在查找某个元素是否存在的过程中,数组和链表都需要挨个循环比较。
数组中如果找到某个值在什么位置,需要循环遍历整个数组,时间复杂度为O(n),而Hash表的时间复杂度基本为O(1)。因为哈希通过一次计算大幅度缩小查找范围,比从全部数据里查找速度要快。
Hash()函数的特点
基本数据结构就介绍到这里了,下面来看一下HashMap如何借助这些简单的数据结构实现高效的
通过上图可以看出,使用Hash函数和数组结构,就可以快速定位Key在数组的上的位置,为了解决哈希冲突,引入了链表来存放冲突的K-V对。
JDK 1.8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。
当 HashMap 中有大量的元素都存放到同一个桶(即数组的一个元素)中时,这个桶下有一条长长的链表,这个时候 HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了它的优势。
HashMap 通过引入红黑树来解决这个问题,使复杂度降到了O(logn).
增加和删除操作
下面简介一下 HashMap 的插入、查找、扩容的基本逻辑
插入逻辑如下:
查找逻辑如下:
扩容操作
扩容过程中几个关键的点:
容量*加载因子
;
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句