List 集合
List 集合是线性数据结构的主要实现,List 集合的遍历结果是稳定的。该体系最常用的是 ArrayList 和 LinkedList。
ArrayList 是容量可以改变的非线程安全集合。内部实现使用数组进行存储,集合扩容时会创建更大的数组空间,把原有数据复制到新数组中。ArrayList 支持对元素的快速随机访问,但是插入与删除时速度通常很慢。
LinkedList 的本质是双向链表。与 ArrayList 相比,LinkedList 的插入和删除速度更快,但是随机访问速度则很慢。除了继承 AbstractList 抽象类外,LinkedList 还实现了另一个接口 Deque,即 double-ended queue,这个接口同时具有队列和栈的性质。LinkedList 的优点在于可以将零散的内存单元通过附加引用的方式关联起来,形成按链路顺序查找的线性结构,内存利用率较高。
Queue 集合
Queue(队列)是一种先进先出的数据结构,队列是一种特殊的线性表,它只允许在表的一端进行获取操作,在表的另一端进行插入操作。自从 BlockingQueue(阻塞队列)问世以来,在各种高并发编程场景中,由于其本身 FIFO 的特性和阻塞操作的特点,经常被作为 Buffer(数据缓冲区)使用。
Map 集合
Map 集合是以 Key-Value 键值对作为存储元素实现的哈希结构,Key 按某种哈希函数计算后是唯一的,Value 则是可以重复的。Map 类提供三种 Collection 视图,Map 和 Collection 之间有种依赖关系。可以使用 keySet() 查看所有的 Key,使用 value() 查看所有的 Value,使用 entrySet() 查看所有的键值对。在多线程并发场景中,优先推荐使用 ConcurrentHashMap,而不是 HashMap。
HashMap 底层是基于数组和链表(解决hash冲突)来实现的。
- HashMap 使用hashCode 和 equals 实现去重;
- HashMap 在多线程环境,存在死链问题和扩容数据丢失问题;
TreeMap 是 Key 有序的 Map 类集合。
- TreeMap 依靠 Comparable 或 Comparator 来实现 Key的去重;
HashMap的存储结构其实就是哈希表的存储结构(由数组与链表结合组成,称为链表的数组)。如下图所示:
Set 集合
Set 是不允许出现重复元素的集合类型。Set 体系最常用的是 HashSet、TreeSet 和 LinkedHashSet 三个集合类。
HashSet 从源码分析是使用 HashMap 来实现的,只是 Value 固定为一个静态对象,使用 Key 保证集合元素的唯一性,但它不保证集合元素的顺序。
TreeSet 从源码分析是使用 TreeMap 来实现的,底层为树结构,在添加新元素到集合中时,按照某种比较规则将其插入合适的位置,保证插入后的集合仍然是有序的。
LinkedHashSet 继承自 HashSet,具有 HashSet 的优点,内部使用链表维护了元素插入顺序。
集合初始化
ArrayList 使用无参构造时,默认大小是 10,后续的每次扩容都会调用 Arrays.copyOf() 方法,创建新数组再复制。
HashMap 容量并不会在 new 的时候分配,而是在第一次 put 的时候完成创建的。HashMap 中有两个比较重要的参数:Capacity和Load Factor,其中Capacity 决定了存储容量的大小,默认为16;而 Load Factor 决定了填充比例,默认0.75。基于这两个参数的乘积,HashMap 内部用 threshold 变量表示HashMap中能放入的元素个数。
为了提高运算速度,设定 HashMap容量大小为 2^n,这样的方式使计算落槽位置更快。此后,每次扩容都是增加2倍。
集合初始化时,指定集合初始值大小。如果暂时无法确定集合大小,那么指定相应的默认值,这要求我们记得各种集合默认值大小,ArrayList 默认大小为 10,HashMap 默认大小为 16。