Java 集合类对比总结
List
ArrayList
- 底层维护一个 Object 类型的数组 elementData[],所以可以放任意类型的元素
transient Object[] elementData
,其中 transient 表示该属性不会被序列化 - 创建 ArrayList 对象时,如果使用无参构造器,那么初始容量为 0,第一次添加扩容为 10,如果需要再次扩容,则扩充容量为当前的 1.5 倍
- 如果使用指定大小的构造器,那么初始容量就为指定的大小,如果需要再次扩容,则扩充容量为当前的 1.5 倍
- 线程不安全
Vector
- 底层也是一个对象数组
protected Object[] elementData
- Vector 是线程同步的,即线程安全
- 扩容机制和 ArrayList 类似,不过是每次容量不足时扩容为当前的 2 倍
LinkedList
- 实现了双端队列和双向链表的特点。底层维护了一个双向链表,有两个指针(first, last)分别指向了首尾节点
- 每个节点里面又维护了 prev、next、item 三个属性
- 添加和删除不是通过数组完成的,效率高;查询和更改的效率低
- 可以添加任意元素,包括 null
- 线程不安全
Set
特点:
- 存放数据是无序的(不是添加的顺序),但是是一个固定的状态(即每次取出都是一样的顺序)
- 不能存放重复元素
HashSet
- 实现了 Set 接口,但实际上是 HashMap(构造器中 new 了一个 HashMap),底层是 数组 + 单链表
- 不保证数据元素有序,取决于 hash 后确定的索引
- 线程不安全
底层添加机制
- 添加元素时,先算出 hash 值
- 根据 hash 值找存储数据表的对应位置,查看是否有元素
- 若没有元素直接插入,添加完成。若有元素,进入下一步
- 调用 equals 比较,如果相同,放弃添加;如果不相同,插到当前链表尾部
底层扩容机制
- 初始时 table 数组(存储元素的表)大小为 16,临界值为 12(加载因子 0.75 * 16)
- 如果 table 使用了 12 个元素,也就是达到临界值,就会扩容到 16 * 2,新的临界值就是 24 ,以此类推
在 Java 8中,如果一条链表的长度到达 TREEIFY——THRESHOLD(默认 8),并且 table 大小 >= MIN_TREEIFY_CAPACITY(默认 64),就会进行树化(红黑树)
LinkedHashSet
- 是 HashSet 的子类
- 底层实现是 LinkedHashMap,底层维护了一个 数组 + 双链表。其中,数组是 LinkedHashMap$Node[]
- 存放的节点类型是 LinkedHashMap$Entry,而 Entry 是一个静态内部类
- 根据 hash 值确定存储位置,通过链表保证元素的插入次序
- 线程不安全
TreeSet
- 会对集合内的元素排序,底层使用的是 TreeMap
- 默认升序,可以在初始化的时候添加比较器(匿名内部类,指定排序规则)
Map
- 用于保存 key-value 对,其中 key 和 value 可以是任意类型的对象,会被封装成 HashMap$Node
- key 不允许重复
- value 可以重复
- key 和 value 之间存在一对一对应,一对 key-value 放在一个 Node 中,因为 Node 实现了 Entry 接口,所以也说一对 key-value 就是一个 Entry(遍历时会有 entrySet 或者 keySet 或者 values 方法,获取对应的集合,集合内容其实就是对 HashMap$Node 节点的引用)
遍历方法
- 获取 keySet 使用增强 for
- 使用 keySet 迭代器获取 key,get 方法获取 value
- 获取 values,使用增强 for
- values 的迭代器
- 获取 entrySet,使用增强 for 遍历(常用)
- entrySet 的迭代器
HashMap
- 线程不安全
- key 和 value 都可以为 null,不过只能有一个 key 为 null
- 底层机制与 HashSet 基本相同
LinkedHashMap
- HashMap 的子类
- 底层机制与 LinkedHashSet 基本相同
TreeMap
- 与刚刚提到的 TreeSet 类似
Hashtable
- key 和 value 都不可以为 null
- 线程安全
- 方法和 HashMap 基本一致
- 底层有数组 Hashtable$Entry ,初始化大小为 11,临界值为 11 * 0.75 约等于 8。
- 当容量(每次添加节点后容量才 +1)大于等于临界值时进行扩容(扩容比较在添加新节点前),扩容方法为 旧的容量 * 2 + 1
Properties
- 继承自 Hashtable
- Properties 可以从 xxx.properties 文件中加载数据到 Properties 类的对象中
- xxx.properties 文件通常为配置文件
对比总结
底层用数组实现的
- ArrayList,底层维护一个 Object 类型的数组 elementData[],所以可以放任意类型的元素
数组 + 单链表
- HashSet
- HashMap
数组 + 双链表
- LinkedHashSet
- LinkedHashMap
可以添加 null 作为元素的
- ArrayList,底层将 null 转换为 “null”
- LinkedList
- Vector
- HashSet,不过只能放一个
- HashMap key 和 value 都可以为 null,不过只能有一个 key 为 null
线程不安全的
- ArrayList
- LinkedList
- HashSet
- LinkedHashSet
- HashMap
线程安全的
- Vector
- Hashtable