由浅入深逐步往里面研究;
[](()List
================================================================
ArrayList 底层由数组组成; 事件复杂度查询是O(1): 插入是O(N)
每次插入的时候会去检查是否需要扩容;非线程安全的;
如果单单调用add(Object)时间 复杂度是O(1);
因为它会在数组尾部进行数据的添加;
优化方案:提前知晓数组长度 提前扩容数组;减少扩容次数;
LinkedList: 底层是由链表组成; 时间复杂度 查询O(N) 插入 删除是O(1)
链表不是数组; 链表是另外一种数据结构; 节点以对象Node进行组装;
每个Node 都记录着上面一个Node信息 以及下面一个Node信息;
因此查找的时候需要每个Node 循环查找下去; 这样查询时间复杂度就是O(N)
插删及是插拔模式。 即改变其Node的上个节点信息 以及他的下个节点信息 即可做成替换
linkedList 是一个双向链表; 下一个节点next 上一个节点prev
ArrayList 以及LinkedList 同样使用 add方法; 其效率 主要是看ArrayList的扩容机制; 在LinkedList里面 是不存在扩容机制的。 在ArrayList里面的扩容机制。 如果一开始 就知道ArrayList 的容量体积大小; 那么ArrayList 的效率 一定是比 LinkedList 高。 这一点如果面试官面到了 需要重点回答的;
[](()Set
===============================================================
set底层是使用 Map的key 我们看一下源码;
PRESENT 是
一个空对象;
所以我们重点放在Map;
[](()Map
===============================================================
HashMap: 我们再开发中最常用到的一个结构。 底层 是由 数组,链表,红黑树 组成(1.8)尾插法
1.7是由 数组 链表 组成; 使用的是头插法
以下一张图带你们了解整个HashMap 以及其底层代码;
图内字有点小:
-
数组长度 :数组长度初始长度为16
-
数组的扩容因子是0.75,例如长度为16 ,当长度到达12的时候扩容
-
树化长度必须要大于64
-
为什么HashMap扩容会是原先的2倍,业务其容量是N的2次幂;
-
Node 会先Hash 散列算法 算出下标;然后把相对应的Node 放到下标处
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 6. 如果Node散列出相同位置;那么该位置就会形成一个链表;
以下是Map 节点 链表与红黑树之间的转换 以及转换阈值 红黑树对比链表 由什么好处?
链表大家都知道 如果要查询的情况 肯定是第一位开始往下查 链表 是查询慢的一种数据结构;
但是 红黑树; 不一样。
红黑树 我们遵从2个原则 就是 左小 右大 ; 我们从根节点开始比对;
如果比根节点下 就从左边往下找; 如果比跟节点大 就从右边往下找。
如果数据量足够大;查找效率就会比链表快接近一倍;
红黑树有一个左旋操作 因此红黑树是有个插入效率问题的;
在后面我们数据结构与算法篇章 我会提出;
总结:
在HashMap 这个数据结构中。 在面试期间问的相当多。
HashMap在我们业务逻辑的场景下 也使用的非常非常多;
我们必须要掌握;
补充:
在JDK 1.7 Map使用的是 头插法 put的对象 在hash相同的情况下 ;
那么就会插入到Node.pre 在多个Thread 的情况下; 就会产生 A覆盖B B覆盖A;
进入死循环的情况 、
在JDK1.8 Map使用的是尾插法 put的对象 在hash相同的情况下;
会插入到Node.next 在多个Thread的情况下;修复了 AB问题;
ConcurrentHashMap:是线程安全的; 在变成链表头部 就会进行加锁 锁粒度是代码块级别的;
首先 我们发现
ConcurrentHashMap 采用了懒加载的方式 在他的创建中
不会有任何操作 知道put的数据 才会初始化整个容器;