Java集合源码分析之开篇

初衷

Java集合是我们使用最频繁的工具,也是面试的热点,但我们对它的理解仅限于使用上,而且大多数情况没有考虑过其使用规范。本系列文章将跟随源码的思路,分析实现的每个细节,以期在使用时避免各种不规范的坑。在这里,我们会惊艳于开发者优秀的设计,也会感激先辈们付出的艰辛努力,更重要的是知其所以然,少犯错误,写出优秀的代码。

许多人对集合类的理解是暴力的,当需要保存对象时就使用ArrayList,当需要保存键值对时就使用HashMap,当需要不可重复时就使用HashSet,等等。而且使用方式也比较单一:

List<String> list = new ArrayList<>();

Map<String, String> map = new HashMap<>();

Set<String> set = new HashSet<>();

// ...

这里我们先不考虑多线程安全问题,这个问题通常有专门的类实现,或者可以通过Collections.synchronizedXXX方法解决。除此之外,我们真的可以如此简单的使用集合吗?

假如数据只有几百、几千个,那么使用何种方式实现差别并不大。但当我们需要处理大数量级的数据时,采用不同的方式效率可能相差百倍甚至更多,这种情况下性能将变得格外重要。例如分别存储于ArrayListLinkedList的100万条数据,要获取位于位置 i 的元素,前者可以瞬间完成,后者则可能需要数秒。这时,使用哪个集合类,怎样合理使用就是我们必须掌握的技能了。

为什么要读本系列文章

如果你也像以上这般使用集合,或者不知道如何优化集合的使用,你都应该读本系列文章。如果你仅有一些点不清晰,也可以在这里找到答案。或者你只是不想阅读枯燥的源码,却对原理很好奇,你也可以阅读本系列文章。如果你只是想应付面试,我想当你坚持把这些文章读完后,你会觉得面试好像也不那么重要了。

本系列文章立足于深刻理解Java集合的原理与实现,读完这些文章后你将获得以下知识:

  • 大量的数据结构知识。

  • ArrayList有那么多构造函数,使用不同的构造函数会有区别吗?

  • ArrayList是如何扩容的?

  • LinkedList如何提供通过位置获取数据的功能的,它的查询效率真的非常低吗?

  • 用数组可以实现队列吗?

  • 影响HashMap性能的因素有哪些?

  • 复杂的红黑树是如何实现的?

  • LRUCache的底层原理是什么?

  • ……

基础知识概述

对数据的操作,大抵就是,以及在某些时候根据位置获取数据,有时可能还需要进行排序又可以理解为一致的操作,因为要修改一条数据需要先找到它,然后替换即可。接下来我们就从这三点简要分析下当前使用比较广泛的几种数据结构。

数组


数组在内存中占据一段连续的内存,所有的数据在内存中连续排列。它的大小是固定的,这一特性使得数组对于插入操作并不友好,我们分析ArrayList时就会看到这种操作的复杂。但数组对于位置的访问是极其友好的,它支持所谓RandomAccess特性,这使得基于位置的操作可以迅速完成,其时间复杂度为O(1)。数组的数据顺序与插入顺序一致,所以查询操作需要遍历,其时间复杂度为O(n)

所以数组最大的优势在于基于位置的访问,在扩展性方面表现无力。

链表


不同于数组,链表是通过指针域来表示数据与数据之间的位置关系的,所以链表在头部或尾部插入数据的复杂度仅为O(1)。链表不具备RandomAccess特性,所以无法提供基于位置的访问。其查询操作也必须从从到尾遍历,复杂度为O(n)

所以链表最大的优势在于插入,而查询的表现很一般。

那有没有一种结构能够结合数组和链表的优点,使得查询和插入都具有优秀的表现呢?答案是肯定的,这就是散列表。

散列表


散列表就是Hash Table,这种结构使用key-value形式存储数据,我们经常使用的HashMapHashTable就基于它。

数组和链表在查询时表现一般的原因在于它们并不记得数据的位置,所以只能用待查询的数据和存储的数据依次比对。散列表使用一种巧妙的方式来减少甚至避免这种依次比对,它的原理是通过一个函数把任何的key转为int,每次查找时只需要执行一次这个函数便可以迅速定位。这个过程是不是像查字典呢?

散列表并不像上述那般完美,因为并不会有一个函数,能够保证所有的key转换结果都不同,也就是会发生所谓的哈希碰撞,而且它必须依赖于其他的数据结构,这部分知识会在后续文章中详细介绍。

良好设计的散列表可以使等操作的时间复杂度均为O(1)

二叉排序树


二叉排序树是解决查询问题的另一方案,如果数据在插入时是有序的,在查询时就可以使用二分法二分法的原理很简单,比如猜一个在0-100之间的数,第一次猜50就可以直接排除一半的数据,每次按照这个规则就可以很快的获取正确答案。二分法的时间复杂度为O(lg n)

树的结构对二分法有天然的支持(但这不是树最重要的用途)。二叉排序树牺牲了一部分插入的时间,但提高了查询的速度,同时有序的数据也可以做些其他的操作。如果查询的操作重要性超过了插入,我们应该考虑这种结构。二叉排序树也存在一些不平衡导致效率下降的问题,所以有了AVL树红黑树,以及用于数据库索引的B树B+树等概念,关于二叉排序树的知识也会在后续文章中介绍。

分析过程

以上介绍的数据结构的知识是我们理解Java集合类的基础,掌握这些核心原理,我们分析集合类源码时才不会吃力,我们会先对这些数据结构进行简要介绍,其他和本系列文章无关的概念不会涉及,大家可以查阅相关专业书籍进行系统学习。

由于集合类的源码十分庞大,从接口抽象设计到具体实现涉及到数十个类,我们不可能每行代码都进行分析,一些在前面分析过的点在后续部分也会略过,但对于我们应该注意的点都会详细解读。有一些过于复杂的代码,还会用图示进行直观的演示,以帮助理解整个运行机制。

文章中会不可避免地粘贴大量源码,但所有部分都会加上详细的中文注释。另外,粘贴的代码不会截取(某些没必要的会删除),这样便于理解,而不用想看哪行代码再去源码中寻找了。

学习源码的实现仅是我们的目的之一,我们更应该掌握作者优秀的编程思想,理解这样做的初衷,站在更高的角度思考问题。

本系列文章的源码全部基于JDK1.8,不同版本的实现代码可能稍有差别,但核心思想是一致的,希望大家不要被具体的实现带偏了路。

Java集合类分为两大部分:CollectionMapCollection又主要由ListQueueSet三大模块组成。本系列文章也会基于这样的结构进行,我们会先了解一些用到的数据结构,然后按照从接口抽象到具体实现的顺序来一步步揭开集合的神秘面纱。

由于Set的结构与Map完全一致,且Set的内部都是基于对应的Map实现的,所以只需要知道Set是什么即可,其具体实现如果感兴趣可以自行阅读源码。

本系列文章不考虑多线程安全问题,与多线程相关的问题十分复杂,以后会对它专门研究。

本系列文章长达20多篇,全部读完需要一定的耐心,但是我相信读完对数据结构和集合一定会有更深的理解,在使用时需要注意哪些点也一定会胸有成竹。

另外由于个人能力有限,文章中若有表达不清晰或解释错误的部分,希望各位看官能够给予批评指正。

目录结构

本系列文章会按照下述结构搭建:

  • 数据结构

  • Iterable概述

  • Collection概述

  • List系列分析

  • Queue系列分析

  • Map概述与系列分析

  • Set简介

以下是全部文章链接:

Java集合源码分析之基础(一):数组与链表

Java集合源码分析之基础(二):哈希表

Java集合源码分析之基础(三):树与二叉树

Java集合源码分析之基础(四):二叉排序树

Java集合源码分析之基础(五):平衡二叉树(AVL Tree)

Java集合源码分析之基础(六):红黑树(RB Tree)

Java集合源码分析之Iterable概述

Java集合源码分析之超级接口:Collection

Java集合源码分析之List(一):超级接口List

Java集合源码分析之List(二):ArrayList

Java集合源码分析之Queue(一):超级接口Queue

Java集合源码分析之Queue(二):接口Deque

Java集合源码分析之Queue(三):ArrayDeque

Java集合源码分析之LinkedList

Java集合源码分析之Map(一):超级接口Map

Java集合源码分析之Map(二):接口SortedMap

Java集合源码分析之Map(三):接口NavigableMap

Java集合源码分析之Map(四):TreeMap

Java集合源码分析之Map(五):HashMap

Java集合源码分析之Map(六):LinkedHashMap

Java集合源码分析之Set概述

本系列文章全部更新完毕,感谢您的关注~


我是飞机酱,如果您喜欢我的文章,可以关注我~

编程之路,道阻且长。唯,路漫漫其修远兮,吾将上下而求索。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容