Java 集合架构图如下所示:
这里空心三角实线箭头表示泛化,其实就是父子类之间的继承关系、空心三角虚线箭头表示实现,就是接口与实现类之间的关系、双向箭头表示关联,是对象之间的一种引用关系、虚线箭头表示依赖,通常表现为类的方法参数用到被依赖对象
如图所示,最顶层 Iterator 表示迭代器,它实现了遍历集合的方法,通常在代码中这样使用:
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
list.add(i);
}
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next());
}
核心在于 hasNext() 和 next() 方法:
- hasNext():检查后面还有没有元素,
- next():获取下一个元素
一般情况下使用 for 循环遍历,但 for 循环遍历时不能删除元素,否则报错,此时使用迭代器可以完美解决问题
ListIterator 和 Iterator 类似,都是迭代器,方便遍历集合,区别在于 iterator 只能单向遍历,ListIterator 可以双向遍历
Collection 接口主要定义保存单个元素集合的常用方法,如 contains()、add()、remove() 等,而 Map 接口主要定义保存键值对元素集合的常用方法,两者保存的元素类型不同。
Collection 接口子类有 List、Set 和 Queue,其中 List 集合主要维护元素顺序,Set 集合主要维护元素唯一,Queue 主要维护队列实现
List 接口最常见实现类有 ArrayList 和 LinkedList,前者通过数组实现,后者通过链表实现:数组实现易于下标访问,在物理地址上连续,链表实现易于增删操作,两者各有利弊且线程不安全
Vector 类和 ArrayList 类方法基本相同,区别在于 Vector 方法通过 synchronzied 修饰,可以保证线程安全
Stack 类继承 Vector 类,表示栈,先进后出,它的大多数方法通过 synchronzied 修饰,线程安全
Set 接口最常见实现类有 HashSet、TreeSet,LinkedHashSet,HashSet 基于 HashMap 实现,TreeSet 基于 TreeMap 实现,LinkedHashTable 基于 LinkedHashMap 实现,这块在 Map 模块再说
Queue 接口常见实现类有 LinkedList,ArrayDeque,两者都实现 Deque 接口,Deque 接口继承 Queue 接口。传统 Queue 表示单向队列,Deque 表示双向队列。这里ArrayDeque 通过数组实现,LinkedList 通过链表实现
通常情况下,保证同步操作的方法由于加解锁原因,效率更低,单线程场景下,使用基础数据类型即可
Map 接口主要定义保存键值对集合常用方法,如 put()、get() 等。Collection 一般用于保存数据,而 Map 一般用于维持关系
Map 接口常用实现类有 HashTable 和 HashMap,两者都通过数组 + 链表实现。JDK 1.8 之后,HashMap 引入红黑树:当某个节点链表长度大于 8 时,数据结构从链表转红黑树。HashTable 并未引入红黑树,但它的方法包含同步处理,线程安全,但效率更差
WeakHashMap 和 HashMap 基本相同,区别在于 WeakHashMap 弱键可以回收,也就是键值采用弱引用,类似前面我们提到的 ThreadLocal 实现原理。HashMap 键使用强引用,即使外部没有强引用指向,也无法被 GC 回收,除非手动删除。
LinkedHashMap 提供维护顺序的功能,可以获取第 n 次 put() 的键值对,TreeMap 提供排序功能,底层通过红黑树实现。
简单来说,Set 基本都通过 Map 实现,区别在于 Set 只用到键,value 都为空。主要通过 Map 键的唯一性保证元素唯一
最后简单聊聊 Collections 和 Arrays:它们和 Collection 以及 Array 不同,仅仅表示工具类。Arrays 内部维护了大量操作数组的方法,Collections 内部维护了大量集合操作方法,这些方法都是静态方法,方便通过类名直接引用