主题
Java 集合框架 已完结
集合概要
常用的集合类及其区别?
集合分为 Collection 容器 和 Map 映射表两种
常见的为:
- List 顺序表
- Set:不重复的集合
- Queue:队列
- Map:存储键值对
哪些集合类是线程安全的?
Vector、HashTable、Stack 都是线程安全的,其他的是非线程安全的。
在 concurrent 并发包中(java.util.concurrent)有着对应的线程安全类,比如 HashMap 有着对应的 ConcurrentHashMap。
什么是 fail-fast,什么是 fail-safe
提示
fail-fast 快速失败:并发修改异常。
fail-safe 失败安全:允许并发修改,遍历前会先复制副本。
- fail-fast 是快速失败。java.util 包下的集合类都是快速失败机制的。在遍历集合时修改集合,会导致并发修改异常。
- fail-safe 是失败安全。采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容。java.util.concurrent 包下的容器都是安全失败的。
Collection 和 Collections 的区别
- Collection 是集合的一个接口,定义了基本的集合操作。
- Collections 是一个工具类,提供了很多静态方法来操作集合。
List、Map、Set 的区别
什么是迭代器 Iterator
迭代器是一个用来遍历集合中的元素的对象。
- 在 fail-fast 机制下,迭代过程中移除元素会抛出异常
- 在 fail-safe 机制下,允许移除元素并且不会抛出异常
List
ArrayList 和 Array 的区别
提示
容量、扩容、API、存储类型
- ArrayList 是动态数组,没有长度限制,不需要事先定义长度;Array 是静态数组,需要提前定义数组的长度
- ArrayList 可以动态扩容;静态数组不能修改
- ArrayList 提供操作 API ,操作方便
- ArrayList 只能存储对象,Array 可以存储对象和基本类型
ArrayList 和 Vector 的区别
提示
线程安全、性能、扩容
- 线程安全:ArrayList 是线程不安全的,Vector 使用了 Synchronized,是线程安全的。
- 性能:ArrayList 强于 Vector
- 扩容:ArrayList 每次会增加一半,Vector 会增加一倍
ArrayList 和 LinkedList 的区别
提示
底层数据结构、性能、空间
- 数据结构:ArrayList 使用数组,LinkedList 使用双向链表
- 性能:ArrayList 对于查询性能高,LinkedList 插入删除性能高
- 空间:ArrayList 会存在预留位置,LinkedList 数据需要携带前驱后继。
说一说 ArrayList 的扩容机制
- ArrayList 创建时大小为 0,第一次添加元素会使用默认大小 10
- 当 ArrayList 存满仍需添加时,会扩容到 1.5 倍,之后拷贝数组
数组和 List 之间的转换
- 数组转List:Arrays.asList()
- List 转数组:List 自带的 toList() 方法
Array.asList 方法的返回的 ArrayList 不是常见的 ArrayList,没有 add、remove 等操作。
解决方法:使用 new ArrayList() 包裹。
Map
说一下 HashMap 的工作原理
HashMap 基于哈希表,将键的哈希值映射到数组的索引。通过链表和红黑树解决哈希冲突。
说一下 HashMap 的 put 的流程
- 判断 Table 是否为空,为空则扩容为默认大小 16
- 通过 hashCode 获取哈希值计算并 Table 索引,插入到链表或红黑树中
- 如果容量达到 64 且链表长度大于 8 时会转换为红黑树(当长度减少到 6 及以下时,红黑树会转换回链表)
- 如果现有元素个数大于 (容量 * 扩容因子0.75) 会进行扩容,扩容一倍。
HashMap 的长度为什么时 2 的幂次方
哈希表使用哈希值取模运算来确定元素存储的位置,当 HashMap 长度是 2 的幂次方时,取模操作能转化为位运算,提高操作效率。
并且扩容时,不需要重新计算 hashCode,只需要查看最高位为 1 还是 0。
HashMap 多线程操作导致死循环问题
JDK 1.7 之前的 HashMap 在两个线程同时进行扩容时,由于头插法会导致链表顺序改变,从而形成环形链表,无法查询。
JDK 1.8 修复了这个问题,使用尾插法。仍会造成并发问题,比如值被覆盖等。
多线程操作时应该使用 ConcurrentHashMap。
HashMap 和 HashTable 有什么区别
- 线程安全:HashMap 是线程不安全的,HashTable 线程安全
- 效率:HashMap 效率比 HashTable 高
- 扩容和大小:HashMap 默认 16,扩容到 2 倍;HashTable 默认 12,扩容到 2倍 + 1
LinkedHashMap 是什么?怎么实现的
在 HashMap 的基础上维护一个双向链表,记录插入顺序
为什么 ConcurrentHashMap 比 HashTable 效率要高?
提示
分段锁、分段哈希、读操作不需要锁
- ConcurrentHashMap 底层使用了分段 Table,使用分段锁;HashTable 使用全局锁
- HashTable 所有方法都获取锁,ConcurrentHashMap 在读操作时不需要获取锁
- HashTable 扩容时需要全局哈希,ConcurrentHashMap 可以分段逐步扩容
Set
Comparable 和 Comparator 的区别
Comparable 接口和 Comparator 接口都是用于排序的接口。
- Comparable 内部排序器:类实现接口,实现
compareTo
方法,可实现排序。 - Comparator 外部排序器:定义单独的实现类,实现
compare
方法,在排序时传入。
HashSet 的实现原理
底层使用 HashMap 实现。集合元素作为 key 保存,value 为静态 Object 对象。
比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同
相同点:
- 保证元素唯一,线程不安全
不同点:
数据结构:
- HashSet 是哈希表 + 链表 + 红黑树
- LinkedHashSet 是双向链表 + 哈希表
- TreeSet 是红黑树
有序性:HashSet 无序;LinkedHashSet 插入顺序;TreeSet 自定义排序。
Queue
Queue 和 Deque 的区别
Queue 是队列,只能先进先出
Deque 是双向队列,两个方向都能添加或删除元素
在 Queue 中 poll 和 remove 有什么区别
- 相同点:都是返回第一个元素,并删除。
- 差异:如果没有元素,poll 会返回 null,remove 会抛异常。
ArrayDeque 和 LinkedList 的区别
ArrayDeque 和 LinkedList 都实现了 Deque 接口,具有队列的功能。
- ArrayDeque 维护了一个环形数组和首尾指针,LinkedList 基于双向链表实现。
- ArrayDeque 插入可能需要扩容;LinkedList 插入需要申请新空间,性能较差
- ArrayDeque 不支持存储 NULL,LinkedList 支持。
说一说 PriorityQueue
PriorityQueue 是优先队列,每次取出的元素都是队列中优先度最大的。
- 基于二叉堆结构来实现,使用可变长数组来存储数据。
- 通过堆元素的上浮和下沉,实现 O(logn) 复杂度内插入和删除队顶元素。
- 非线程安全。
- 默认是小顶堆,可以通过传入 Comparator 作为构造参数定义优先级。