1. 集合概览
容器主要包含 Collection 和 Map 两种
框架底层数据结构
List
ArrayList:Object[]数组Vector:Object[]数组,线程安全LinkerList: 双向链表
Set
HashSet:基于 HashMap 的实现,底层采用 HashMap 来保存元素LinkedHashSet: HashSet 的子类,内部是通过LinkedHashMap来实现的TreeSet(有序): 红黑树
Map
HashMap: 由数组+链表组成的(JDK1.7),JDK1.8 增加红黑树,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。LinkedHashMap: 继承自 HashMap,增加一条双向链表,使得上面的结构可以保持键值顺对的插入顺序,同时对链表进行相应的操作实现访问顺序。Hashtable: 数组+链表组成TreeMap: 红黑树
如何选用集合
- 需要根据
key获取元素时选用Map,一般使用HashMap,需要排序使用TreeMap,需要保持顺序使用感LinkedHashMap,需要线程安全使用ConcurrentHashMap - 只需要存放元素是就使用
Collenction接口的集合,一般使用实现List接口的比如ArrayList和LinkedList,需要保证元素唯一使用实现Set接口的集合,比如HashSet或TreeSet
2. ArrayList
ArrayList 实现了 List 接口,底层通过数组实现,有一个容量(capacity),代表底层数组的实际大小,默认值为 10。向容器添加元素时,若容量不足,则自动增加底层数组的大小,为原先的 1.5 倍。
ArrayList 与 LinkedList 区别?
- ArrayList 底层使用 Object 数组,LinkedList 底层使用双向链表
- ArrayList 可以快速随机访问
- 对于插入和删除操作,LinkedList 优于 ArrayList
- 内存空间占用:LinkedList 的每一个元素都要存储前驱和后继的引用,ArrayList 需要预留一定的容量空间
Fail-Fast 机制
Fail-Fast 是快速失败机制,通过记录 modCount 参数来实现,在面对并发修改,或者在遍历过程中修改容器元素,迭代器很快就会完全失败,抛出异常
3. HashMap 的底层实现
JDK1.8 之前 HashMap 底层是 数组加链表 的结合。HashMap 通过 key 的 hashCode() 经过扰动函数之后得到 hash 值,然后通过 (n-1) & hash 判断当前元素的存放位置,如果不存在直接存放,如果当前位置存在元素,判断该元素与存入的元素 hash 值和 key 是否相同,如果相同直接覆盖,不相同通过拉链法解决冲突。
JDK1.8 之后在解决哈希冲突发生了变化,当链表长度大于阈值(默认 8),将链表转换为红黑树。
HashMap 的数组长度为什么是 2 的幂次方
因为存放元素的位置是通过 (n - 1) & hash 计算的,n 是数组长度。对除数取余的操作,如果除数是 2 的幂次方等价于与其除数减一的与(&)操作,也就是 hash%length==hash&(length-1),而且 & 相对于 % 运算效率高。
ConcurrentHashMap
JDK1.7 使用分段锁实现,JDK1.8 使用数组+链表+红黑树数据结构和 CAS 原子操作实现 ConcurrentHashMap