Java集合
集合表示一组对象,称为它的元素。一些集合允许重复元素,而另一些则不允许。有些是有序的,有些是无序的。
Collection 与 Iterator
Java 集合的顶层接口是 java.util.Collection<E>
,它作为集合层次结构中的根接口,定义了集合应有的一些基础方法,例如 add, clear, contains 等方法。
Collection 接口继承了 Iterable 接口,该接口的 iterator() 方法会返回一个迭代器,可以遍历集合中的元素。
Iterator 接口是集合上的迭代器。迭代器在Java集合框架中代替枚举。迭代器与枚举有两个不同之处:
- 迭代器允许调用者在迭代期间使用定义良好的语义从基础集合中删除元素。
- 方法名得到了改进。
Iterator 接口主要包含以下三个方法:
方法 | 作用 |
---|---|
hasNext() | 判断迭代器是否还有元素 |
next() | 获得迭代器的下一个元素 |
remove() | 删除迭代器返回的最后一个元素 |
List
List 是有序的集合,用户可以精确地控制每个元素在列表中的位置。用户可以通过元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。列表中的元素是可以重复的。
ArrayList
ArrayList 是基于数组实现的 List 类,封装了一个动态增长的 Object 数组,支持元素的随机访问,适合查找和遍历,不适合插入和删除。ArrayList 默认大小为 10, 扩容为 1.5 倍扩容。
ArrayList 虽然线程不安全,但是它的父类 AbstractList 定义了一个 modCount 的变量,记录了这个列表在结构上被修改的次数。如果该字段的值意外更改,迭代器(或列表迭代器)将抛出一个 ConcurrentModificationException 。
例如在 ArrayList 的 writeObject 方法中,通过这个变量确保在多线程情况下列表序列化时结构更改时会抛出异常让调用者得知:
1 | private void writeObject(java.io.ObjectOutputStream s) |
LinkedList
LinkedList 是双链表实现的list和Deque接口。实现所有可选的列表操作,并允许所有元素(包括null)。
Deque 接口是双端队列接口,可以在队列两端操作元素。
Vector
Vector 是线程安全版本的 ArrayList,但是性能低下,不常用。
CopyOnWriteArrayList
一个线程安全的ArrayList变体,其中所有可变操作(添加、设置等)都是通过生成底层数组的新副本来实现的,即写时复制策略。
例如它的 add 函数如下:
1 | public boolean add(E e) { |
但是 CopyOnWriteArrayList 会产生数据弱一致性问题,即读的线程可能不会立即感知到写线程对数据的修改。
Map
Map 接口不是 Collction 接口的子接口,它以 key-value 的形式保存数据。
HashMap
HashMap 是基于哈希表实现的映射接口,它不能保证内部元素的顺序。
HashMap 的默认容量大小是16,负载因子是0.75,且 HashMap 的大小一定为2的幂次方。因为容量为2的幂次方时,当需要扩容时,很容易计算出元素再哈希的值。
在 jdk1.8 之后,HashMap 的哈希扰动函数如下:
1 | static final int hash(Object key) { |
HashMap 处理哈希冲突的方式是链表法,在1.8之后,当链表长度大于8时,会转为红黑树,当红黑树节点个数小于6时,会转为链表。因为红黑树相比较链表更加占用空间,所以当链表到达一定长度时才转换。
HashMap 在 jdk1.8 之前采用头插法,多线程下可能会造成环,jdk1.8 之后采用尾插法,不会造成环,但是多线程下依旧不安全。
LinkedHashMap
LinkedHashMap 继承 HashMap,它维护了一个双向链表。
当它的属性 accessOrder 为 true 时,表示访问顺序,某一个节点被访问时将它移动到链表尾部,这样链表头就是最久未使用节点。当它的属性 accessOrder 为 false 时,表示插入顺序,某一个节点插入时将它移动到链表尾部,这样链表头就是最早插入的节点。
可以通过继承它实现最简单的缓存:
1 |
|
Set
Set 接口表示不包含重复元素的集合。
HashSet
HashSet 实现了Set接口,它由一个哈希表(实际上是一个HashMap实例)支持。它不能保证集合的迭代顺序;特别是,它不能保证顺序随时间保持不变。这个类允许空元素。
LinkedHashSet
LinkedHashSet 继承自 HashSet,它实际上由一个 LinkedHashMap 实例支持。能维护元素的插入次序。
TreeSet
TreeSet 通过红黑树维护元素的插入次序。