1.Map介绍
Map–>映射 key获取value
1.1方法
V put(K key,V value):添加元素,如果当前key对应有value值,则替换掉并返回旧value值
void clear():移除所有键值对元素
V remove(Object key):根据key值删除键值对,返回value值
boolean containsKey(Object key):判断集合是否含有该键值
boolean containsValue(Object value):判断集合是否包含指定的值
boolean isEmpty():判断集合是否为空
Set<Map.Entry<K, V>> entrySet():返回的是键值对对象的集合
V get():根据键获取值
Set keySet():获取集合中所有的键的集合 (key不会重复用Set)
Collection values():获取集合中所有值的集合 (values可能会重复)
int size():返回集合中键值对的对数
1.2Map子类
2.散列表(哈希表)介绍
无论是Set还是Map,我们会发现都会有对应的–>HashSet,HashMap
回顾一下数据和链表:
- 链表和数组都可以按照人们的意愿来排列元素的次序,他们可以说是有序的(存储的顺序和取出的顺序是一致的)
- 但同时,这会带来缺点:想要获取某个元素,就要访问所有的元素,直到找到为止。
- 这会让我们消耗很多的时间在里边,遍历访问元素~
而还有另外的一些存储结构:不在意元素的顺序,能够快速的查找元素的数据
其中就有一种非常常见的:散列表
2.1散列表工作原理
为每个对象计算出一个整数,称为散列码。根据这些计算出来的整数(散列码)保存在对应的位置上
在Java中,散列表用的是链表数组实现的,每个列表称之为桶。
一个桶上可能会遇到被占用的情况(hashCode散列码相同,就存储在同一个位置上),这种情况是无法避免的,这种现象称之为:散列冲突
- 此时需要用该对象与桶上的对象进行比较,看看该对象是否存在桶子上了~如果存在,就不添加了,如果不存在则添加到桶子上
- 当然了,如果hashcode函数设计得足够好,桶的数目也足够,这种比较是很少的~
- 在JDK1.8中,桶满时会从链表变成平衡二叉树(红黑树)
如果散列表太满,是需要对散列表再散列,创建一个桶数更多的散列表,并将原有的元素插入到新表中,丢弃原来的表~
- 装填因子(load factor)决定了何时对散列表再散列~
- 装填因子默认为0.75,如果表中超过了75%的位置已经填入了元素,那么这个表就会用双倍的桶数自动进行再散列
3.红黑树介绍
3.1平衡二叉树
上面散列表中已经提过了:如果桶数满的时候,JDK8是将链表转成红黑树(自平衡二叉树)。并且,我们的TreeSet、TreeMap底层都是红黑树来实现的。
树是要“均衡”才能将它的优点展示出来的,就有了平衡树这么一个概念~红黑树就是一种平衡树,它可以保证二叉树基本符合矮矮胖胖(均衡)的结构
3.2 2-3树
插入的时候可以保持树的平衡,平衡树最基础的2-3树长这个样子:
在2-3树插入的时可以简单总结为两个操作:
- 合并2-节点为3-节点,扩充将3-节点扩充为一个4-节点
- 分解4-节点为3-节点,节点3-节点为2-节点
- ………至使得树平衡~
合并分解的操作还是比较复杂的,要分好几种情况,代码量很大~
3.3从2-3树到红黑树
由于2-3树为了保持平衡性,在维护的时候是需要大量的节点交换的!这些变换在实际代码中是很复杂的,大佬们在2-3树的理论基础上发明了红黑树
红黑树是对2-3查找树的改进,它能用一种统一的方式完成所有变换。
红黑树是一种平衡二叉树,因此它没有3-节点。那红黑树是怎么将3-节点来改进成全都是二叉树呢?
红黑树就字面上的意思,有红色的节点,有黑色的节点:
3.4红黑树基础知识
红黑树是在2-3的基础上实现的一种树,它能够用统一的方式完成所有变换。很好理解:红黑树也是平衡树的一种,在插入元素的时候它也得保持树的平衡,那红黑树是以什么的方式来保持树的平衡的呢?
红黑树用的是也是两种方式来替代2-3树不断的节点交换操作:
- 旋转:顺时针旋转和逆时针旋转
- 反色:交换红黑的颜色
- 这个两个实现比2-3树交换的节点(合并,分解)要方便一些
红黑树为了保持平衡,还有制定一些约束,遵守这些约束的才能叫做红黑树:
- 红黑树是二叉搜索树。
- 根节点是黑色。
- 每个叶子节点都是黑色的空节点(NIL节点)。
- 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点(每一条树链上的黑色节点数量(称之为“黑高”)必须相等)。