数据结构
线性表
ArrayList、LinkedList和Vector的异同以及底层数据结构是
| 1、ArrayList 底层:是数组结构,查询快,增删慢,线程不安全,效率高。
2、LinkedList底层:是链表数据结构,查询慢,增删快,线程不安全,效率高。
3、Vector 底层:是数组结构,查询快,增删慢,线程安全,效率低。
|
ArrayList如何动态扩展
1 2 3 4 5 6 7 8 9 10
| 1. ArrayList()会使用长度为 0 的数组 2. ArrayList(int initialCapacity)会使用指定容量的数组 3. public ArrayList(Collection<? extends E> c)会使用 c 的大小作为数组容量
1. add(Object o)首次扩容为 10,再次扩容为上次容量的1.5倍 2. addAll(Collection c) 2.1 没有元素时,扩容为Math.max(10,实际元素个数) 2.2 有元素时,扩容为Math.max(原容量1.5倍,实际元素个数)
|
HashMap
HashMap的底层数据结构
1 2 3
| JDK1.7 数组+链表
JDK 1.8 数组+(链表/红黑树)
|
为何要用红黑树
1 2
| 1.数值较大时,使用红黑树可以优化查询性能 2.可以避免DoS攻击,防止有人恶意使用多个hashcode一样的值注入攻击,避免链表超长性能下降
|
为何一上来不树化
1 2 3 4
| 数值较小时,链表的查询性能、内存占用较优于红黑树,所以不需要一上就树化 查找更新的时间复杂度是O(log<sub>2</sub>n) TreeNode占用空间也比普通Node要大 如非必要尽量使用链表
|
树化阈值为何是8
1
| hash表内按泊松分布,在负载因子是0.75的情况下,长度超过8的链表出现概率是0.00000006,选择8是为了让树化几率足够小
|
何时会树化
何时会退化为链表
1 2
| 情况1:在扩容时如果树被拆分了,且树的元素个数<=6则会退化成链表 情况2:remove树节点时,若移除节点之前root、root.left、root.right、root.left.left有一个为null时,也会退化成链表
|
索引如何计算
1
| 计算对象的hashCode,再进行HashMap的hash方法进行二次哈希,最后hash 求模% 或 & 得到索引
|
为什么要二次哈希
数组容量为何是2的n次幂
1 2 3
| 1.计算索引时,2的n次幂可以使用位与运算取代取模,效率更高 2.扩容时可以根据hash & oldCap == 0 来判断元素是否留在原来位置,否则 新位置 = 旧位置 + oldCap 3.是为了配合索引计算、二次哈希的优化手段,例如HashTable的容量就不是2的n次幂,并不能说那种设计更优,应该是设计者综合了各种因素,最终选择了使用2的n次幂作为容量
|
HashTable与HashMap的区别
| HashMap |
线程不安全 |
允许有null的键和值 |
效率高一点 |
方法不是Synchronize的 |
有containsValue和containsKey方法 |
| HashTable |
线程安全 |
不允许有null的键和值 |
效率稍低 |
方法是Synchronize的 |
有contains方法方法 |
HashMap的扩容机制
1 2 3 4
| 1. HashMap的容量是有上限的,必须小于 1 << 30,也就是2的30次幂 = 1073741824 2. 空参数的构造函数:实例化的HashMap默认内部数组是null,即没有实例化。第一次调用put方法时,则会开始第一次初始化扩容,长度是16 3. 有参构造函数:用于指定容量。会根据指定的正整数找到不小于指定容量的2的幂数,将这个数设置赋值给阈值(threshold)。第一次调用put方法时,会将阈值赋值给容量。 4. 如果不是第一次扩容,则容量变为原来的2倍,阈值也变为原来的2倍。(容量和阈值都变为原来的2倍时,负载因子还是不变)
|
HashMap是不是线程安全的
1 2
| 不是,会发生数据错乱 并发情况下使用ConcurrentHashMap
|
HashMap的put元素的过程
1 2 3 4 5 6 7 8 9
| 1. HashMap是懒惰创建数组的,首次使用才创建数组 2. 计算索引(桶下标) 3. 如果桶下标没有被占用,创建Node占位返回 4. 如果桶下标已经被占用 4.1 如果节点是TreeNode走红黑树的添加或更新逻辑 4.2 如果节点是Node,走链表的添加或更新逻辑 4.2.1 如果添加时超过树化阈值且容量大于64,走树化逻辑 5. 返回容量是否超过阈值,一旦超过就进行扩容 5.1 如果进行扩容,要对旧元素重新计算索引
|
为什么加载因子是0.75
1 2 3
| 1. 在空间占用与查询时间之间取得较好的权衡 2. 大于这个值,空间节省了,但链表会比较长,影响性能 3. 小于这个字,冲突减少了,但扩容会比较频繁,空间占用多
|
Node的数据结构
1 2 3 4 5 6 7
| static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; }
|
寻址算法
1 2 3 4 5
| static final int hash(Object key){ int h; return (key == null) ? 0 : (h = key.hashColde()) ^ (h >>> 16); }
|
树
红黑树的数据结构,左旋和右旋是怎样实现的
- 所有节点非红即黑
- 根节点是黑色
- 叶子节点是黑色
- 不能有连续的红色
- 任意节点到叶子节点路径中有相同数量的黑色节点
1 2 3
| 左旋:当前父结点是红色,叔叔是黑色的时候,且当前的结点是右子树。左旋以父节点作为左旋
右旋:当前父结点是红色,叔叔是黑色的时候,且当前的结点是左子树。右旋
|
什么是Huffman树
1
| 给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
|
Huffman树的用途
Huffman树什么情况下压缩效率高
B树和B+树
1 2 3 4 5 6
| B树也称为B-树
B+树是B-树的一种变体
B+树把数据存放在叶子节点上,并有序排列,这样做刚好适用于范围查询 B+树最终会呈现出一个矮胖地形式,B-树则是高瘦
|
对TreeMap的理解
TreeMap的底层数据结构
1
| TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
|