Java集合框架整理

声明:此系列文章整理自陈树义

 

 

集合系列开篇:为什么要学集合?

集合可以说是学习 Java 中最重要的一块知识点了,无论做任何业务系统,集合总是最为基础的那块 API。我第一次接触集合,是在我大三的时候,那时候去面试,面试官问我:你了解过集合吗?可惜那时候没什么项目经验,所以基本没有了解过,因此也错失了机会。

到了现在,我已经工作了5年了,也做过了大大小小十几个项目。这些项目中有简单的 SSH 项目,也有分布式高并发的复杂项目。无论在哪个项目中,关于集合的时候是必不可少的。但我现在慢慢回顾过去做的项目,我发现自己使用到的集合还是比较少,基本上只有:ArrayList、HashSet、HashMap 这几个。

但当我开始深入去了解 JDK 集合的整个体系时,我发现之前的我了解得确实非常浅显。例如关于 List 的实现有 ArrayList、LinkedList、Vector、Stack 这四种实现,但我们很多时候只是直接使用 ArrayList,而不是根据场景去选择。

1.学习集合源码,能够让我们使用得更加准确。

当我们深入学习了源码之后,我们就能够了解其特性,从而能够根据我们的使用场景去做出更好的选择,从而让我们的代码运行效率更高。

我们举一个最简单的例子 —— ArrayList 和 LinkedList。它们两者底层采用了完全不同的实现方式,ArrayList 使用数组实现,而 LinkedList 则使用链表实现。这使得 ArrayList 的读取效率高,而 LinkedList 的读取效率低。但因为 LinkedList 采用链表实现,所以其增加和删除比较方便,而 ArrayList 则比较麻烦。所以 ArrayList 比较适合用于读场合较多的情况,而 LinkedList 比较适合用于增加、删除较多的场景。

我们来看另外一个例子 —— HashMap 和 TreeMap。乍看之下,他们都是 Map 集合的实现,但是它们内部有着截然不同的实现。HashMap 是 Map 接口的哈希实现,其内部使用了链表和红黑树实现。而 TreeMap 是 Map 接口的有序实现,其内部使用了红黑树实现。所以 HashMap 一般用来存储 key、value 的实现,而 TreeMap 常用存储需要排序的元素。

除了我们举的这两个例子之外,还有许多这样的例子,比如:HashMap 与 LinkedHashMap 的区别,HashMap 与 WeakHashMap 的区别,LinkedList 与 ArrayDeque 的区别。

2.学习集合源码,让我们学习经典的设计方式。

在集合的整个架构设计中,其类继承体系非常简单,但是却很经典。例如:Collection 接口设计了集合通用的操作,每个集合类型都有对应的接口(List、Set、Map),每个集合类型都有对应的抽象实现(AbstractList、AbstractSet、AbstractMap)等。

当我们阅读这些源码的时候,这种设计方式都会潜移默化地影响我们。当我们之后自己设计一个框架的时候,我们就会不知不觉地用上去。所有的创新都是从模仿开始的,所以阅读优秀的集合源码很重要。

3.帮助通过面试,获得更高的薪酬。

现在关于集合的原理是 Java 工程师面试的家常菜,几乎每一个企业的面试都会问到。如果你连这块东西都没搞清楚,那么你就不需要聊其他了,直接被干掉。而如果你能将整个 Java 集合体系清晰地说出去,并且举一反三地对比,那么你就比其他人优秀了。

4.学习经典的数据结构。

还记得大学在学习数据结构的时候,我们都是从理论上去记忆。但是当我看完集合源码之后,我忽然发现——JDK集合源码简直就是数据结构的最佳实践呀!

数据结构中最为基础的几个结构为:顺序表、单链表、双向链表、队列、栈、二叉堆、红黑树、哈希表。这些所有的实现都能在 JDK 集合的实现中找到。例如:ArrayList 就是顺序表的实现,LinkedList 就是双向链表的实现,Stack 就是栈的实现,HashMap 就是哈希表的实现,TreeMap 就是红黑树的实现,PriorityQueue 就是二叉堆的实现。

5.所有技术的基础

集合源码可以说是 JDK 所有源码中最为简单的一块了,而且也是其他所有源码的基础。例如线程池的源码中也大量使用了阻塞队列,如果你连集合源码都搞不懂,那么线程池的源码你也肯定看不懂的。而如果线程池源码看不懂,那么你 netty 的源码也看不懂的。netty 源码看不懂,那么 dubbo 的源码也是看不懂的。

看明白了么?这些技术都是一换扣着一换的。如果你想要后续学习更加快速,那么你就必须把最基础的东西学明白了。如果连最基础的东西都没学明白,就直接去学其他更复杂的东西,最后只会越来越难,最终逃脱不了放弃的命运。

读到了这里,我相信你也对集合的重要性有了不一样的认识。那么接下来一段时间,就让我和你一起来深入学学集合源码吧。如果觉得读了有用,那么请给我一个赞吧。你们的赞是我继续写下去的动力!

集合系列(一):集合框架概述

Java 集合是 Java API 用得最频繁的一类,了解其及继承结构,掌握其实现原理非常有必要。总的来说,Java 容器可以划分为 4 个部分:

  • List 集合

  • Set 集合

  • Queue 集合

  • Map 集合

除了上面 4 种集合之外,还有一个专门的工具类:

  • 工具类(Iterator 迭代器、Enumeration 枚举类、Arrays 和 Collections)

在开始聊具体的集合体系之前,我想先介绍一下 Collection 框架的基本类结构。因为无论是 List 集合、Set 集合还是 Map 集合都以这个为基础。

  • 首先,最顶层的是 Collection 接口。

可以看到 Collection 接口定义了最最基本的集合操作,例如:判断集合大小、判断集合是否为空等。List、Set、Queue 都继承了该接口。

  • 接着,AbstractCollection 也继承了 Collection 接口。

从这个类名可以看出,其是一个抽象类。AbstractCollection 对 Collection 接口中一些通用的方法做了实现。例如:判断是否为空的方法、判断是否包含某个元素的方法等。

通过继承 AbstractCollection 接口,可以少写许多不必要的代码,这是代码抽象设计最常用的思想。AbstractCollection 是最为基础的类,其他所有集合的实现都继承了这个抽象类。

List 集合

List 集合存储的是有序的数据集合,其数据结构特点是:读取快,修改慢,适合于读取多、写入修改少的场景。List 集合的类继承结构如下:

我们可以看到除了 Collection 和 AbstractCollection 之外,我们还有 List 接口和 AbstractList 抽象类。其中 List 接口是 List 集合的最上层抽象,其继承了 Collection 接口,表示其实一个集合。而 AbstractList 则是 List 集合的抽象实现,实现了许多公用的操作。

整个 List 集合的实现可以分为红、黄、绿三大块。其中红色部分是 List 集合的列表实现,绿色部分是 List 结合的链表实现,而 黄色部分则是 List 集合列表实现的线程安全版本。

列表实现

ArrayList 类是很常用的 List 实现,其底层是用数组实现的。其读取元素的时间复杂度是 O(1),修改写入元素的时间复杂度是 O(N)。我们将会在下面的章节中详细介绍,这里不做深入。

列表安全实现

Vector 类也是很常用的 List 实现,其数据结构与 ArrayList 非常类似。但其与 ArrayList 的一个最大的不同是:Vector 是线程安全的,而 ArrayList 则不是线程安全的。

Stack 类则是在 Vector 的基础上,又实现了一个双向队列。所以其除了是线程安全的之外,其还是一个先进后出的 List 实现。

最后我们总结一下,List 集合最为关键的几个实现类是:

  • ArrayList:列表集合经典实现。

  • Vector:列表集合经典实现,线程安全,与 ArrayList 对应。

  • Stack:栈结构的经典实现,先进后出的数据结构。继承了 Vector,线程安全。

  • LinkedList:链表结构的经典实现。

链表实现

LinkedList 是一个经典的链表实现。LinkedList 继承了 AbstractSequentialList 抽象类。AbstractSequentialList 抽象类从字面上理解是抽象连续列表。这里的重点是 sequential 这个词,表示其数据结构是连续的(链表)。从其源码注释也可以看出这个意思。

This class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a "sequential access" data store (such as a linked list). (意译)如果你想要实现一个连续存储(链表)的 List,那么这个抽象类可以让你减少不少工作量。

其实从命名就可以看出,AbstractSequentialList 其实是连续列表(链表)的一个抽象实现。AbstractSequentialList 抽象类做了许多工作,使得后续的链表实现更加简单。从 AbstractSequentialList 的注释可以看到,如果要实现一个链表,那么只需要实现 listIterator 方法和 size 方法就可以了。

Set 集合

Set 集合中存储的元素是不重复的,但是其存储顺序是无序的。下面是 Set 集合的类继承结构图:

与 List 集合类似,都是一个 Set 接口继承了 Collection 接口。一个 AbstractSet 抽象类实现了 Set 接口、继承了 AbstractCollection 抽象类。这部分完全和 List 相同。

Set 集合的实现可以分为两大块,一块是 Set 集合的有序实现(红色部分),另一块是 Set 集合的哈希实现(黄色部分)。

有序实现(TreeSet)

  • SortedSet 接口继承了 Set 接口,TreeSet 实现了 SortedSet。

我们知道 Set 集合中的元素是无序的,而 SortedSet 接口则是定义了有序 Set 集合的接口。而 TreeSet 则是 SortedSet 的具体实现。

哈希实现(HashSet、LinkedHashSet)

HashSet 是 Set 接口的经典哈希实现。但 Set 集合中的元素是无序的,为了维护 Set 集合的插入顺序,人们创造出了 LinkedHashSet。LinkedHashSet 是在 HashSet 的基础上用链表维护元素的插入顺序。

到这里我们总结一下 Set 集合的所有实现:

  • TreeSet:Set 集合的有序实现。

  • HashSet:Set 集合的哈希实现。

  • LinkedHashSet:Set 集合的哈希实现,维护了元素插入顺序。

Queue 集合

队列是一个特殊的线性表,其数据结构特点是先进先出。Queue 类结构体系如下图所示:

首先,Queue 接口继承了 Collection 接口。Queue 接口在拥有基本集合操作的基础上,定义了队列这种数据结构的基本操作。可以看到 offer、poll 等方法都是队列独有的操作。

接着,AbstractQueue 是对 Queue 接口的抽象实现。针对队列这种数据结构,其添加、删除元素的动作都不一样。在 AbstractQueue 抽象类里将队列的基本操作都实现了一遍。例如 AbstractQueue 中的 add 方法就和 AbstractList 中的 add 方法有着不同的实现。

如上图所示,Queue 的类结构整体可以分为黄色、红色两个部分。红色部分是 Queue 接口的有序实现,有 PriorityQueue 这个实现类。黄色部分是 Deque(双向队列)的实现,有 LinkedList 和 ArrayDeque 两个实现类。

有序实现

PriorityQueue 是 AbstractQueue 抽象类的具体实现。

PriorityQueue 表示优先级队列,其按照队列元素的大小进行重新排序。当调用 peek() 或 pool() 方法取出队列中头部的元素时,并不是取出最先进入队列的元素,而是取出队列的最小元素。

双向实现

  • 首先,我们会看到 Deque 接口。

Deque(double ended queue)是双向队列的意思,它能在头部或尾部进行元素操作。

  • 最后,我们看到 LinkedList 和 ArrayDeque 都是 Deque 接口的具体实现。

LinkedList 我们之前说过了,是一个链表,但它还是一个双向队列。因此 LinkedList 具有 List 和 Queue 的双重特性。ArrayDeque 是一个双向循环队列,其底层是用数组实现。更多内容,我们将在队列章节讲解。

最后我们总结 Queue 体系的几个常见实现类:

  • PriorityQueue:优先级队列

  • LinkedList:双向队列实现

  • ArrayDeque:双向循环队列实现

Map 集合

Map 集合与 List、Set、Queue 有较大不同,其实类似于 key/value 的数据结构。

  • 首先,Map 接口是最顶层的接口。

与 List、Set、Queue 类似,Map 接口定义的是哈希表数据结构的操作。例如我们常用的 put、get、keySet 等。

  • 接着,有 AbstractMap 抽象类。

和 List 等类似,AbstractMap 是 Map 接口的抽象实现。如上图所示,Map 集合的整个类结构可以分为红、黄、绿三块。

哈希实现

红色部分可以看成是 Map 的哈希实现。

  • AbstractMap 有具体的实现类 HashMap。

HashMap 是 AbstractMap 基于哈希算法的具体实现。

  • 接着,LinkedHashMap 和 WeakedHashMap 继承了 HashMap。

LinkedHashMap 是 HashMap 的进一步实现,其用链表保存了插入 HashMap 中的元素顺序。WeakedHashMap 是 HashMap 的进一步实现,与 HashMap不同的是:WeakedHashMap 中的引用是弱引用,如果太久没用,则会被自动回收。

有序实现

黄色部分可以看成是 Map 集合的有序实现。

  • 首先,SortedMap 接口继承了 Map 接口。

与 Set 一样,Map 中的元素是没有顺序的,So

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值