从JDK源码学习Arraylist
从今天开始从源码去学习一些Java的常用数据结构,打好基础:)
Arraylist源码阅读:

jdk版本:1.8.0
首先看其构造方法:
构造方法一:
第一种支持初始化容量大小,其中声明一个对象数组,赋值给this.elementdata

构造方法二:
第二种无参构造函数,即不指定初始容量大小,则默认赋值this.elementdata为一个空的对象数组,但是由注释可以看到其无参构造实际上初始容量为10


在elementData的注释中也说了该变量是实际存储Arrylist数据的存储结构,任何空的arraylist,当第一次被调用add放进元素时,将会扩充容量为default_capacity也就是10


看看其add方法,因为arraylist也是有序的,因此加入的元素在列表尾部,在添加元素之前,调用ensureCapacityInternal,确保内部容量大小

在ensureCapacityInternal中将判断当前的elementdata的值是否为空数组,若为空则赋值minCapacity为默认容量和入口参数minCapacity的较大值,然后进一步调用ensureExplicitCapacity明确容量大小

在ensureExplicitCapacity中,modCount自增,判断当前最小容量和arraylist的实际元素个数差值若大于零,则调用grow函数来进行实际的容量扩充

扩容函数grow先取到当前arraylist的实际长度,然后将其扩大1.5倍,然后判断该值和最小容量的大小,若扩充1.5倍小于所需要的最小容量,则赋值新的容量为需要的最小容量,此时并判断是否产生溢出情况,也就是注释里面的overflow conscious mode的含义,所以arraylist不是无限扩容,看下其max_array_size的值

数组最大值为integer.max_value-8,也就是2的31次-1-8


至于为什么要-8,这里有些vm要存储其最大值的大小需要八个字节,如下图所示

如果扩充的新容量比max还大,则调用hugeCapacity,判断最小的容量和2的31次-1的大小,若大于则赋值max_value,否则说明此时最小容量介于max_value-8和max_value之间,则赋值为max_value-8

然后调用Array.copyof将旧的arraylist中的值拷贝到新的扩充后的arraylist中,所以默认空数组的add操作后容量即为10
构造方法三:
可以传递任何实现了Collection接口的类,其调用collection的toarray方法返回一个对象数组,也就是将集合中的元素以对象数组形式返回,toarray的注释里也说明了这个方法是array和collection的桥梁

为了防止重写toArray方法返回的并不是对象数组,因此这里判断一下elementData的类是否是对象数组,如果不是的话,则将element中的数组copy到对象数组中
比如有MySubClass是MyClass的子类。
Collection<MyClass> myCollection; //myCollection里有很多元素。
Collection<MySubClass> mySubCollection; //mySubCollection里有很多元素。
ArrayList<MyClass> myList = new ArrayList<MyClass>(myCollection);
也可以:
ArrayList<MyClass> myList = new ArrayList<MyClass>(mySubCollection);
意思就是这里用extends e,来指定定义一个父类的arraylist,则其所有子类的集合都能放进该父类的arraylist,从而编译器才能够知道放入的元素都是满足?也就是,初始定义arraylist的类型声明
关于线程安全:
上面遗留了一个modcount++的自增操作的解释,看一下jdk对modcount的解释

该参数是对arraylist容量大小修改的次数,也就是删减元素改变大小时可能会使正常的迭代过程出现错误,那么针对单线程而言,不存在又读又写,但在多线程情况下,可能存在读写同时进行的操作,参考知乎一个很精简明确的答案,看完真的是一目了然,如果结构发生变化则抛出ConcurrentModificationException


通过调用上面这个方法来判断是否结构发生变化,调用add remove时都将修改modcount,通过迭代时先保存一份modcount,若迭代过程中再取modcount和保存的值不等则抛出异常
总结:
①.初始不指定容量时设置为10
②.每次扩充为实际长度的1.5倍与所需最小容量比较
③.arraylist是非线程安全的
④.其最大值为2的31次-1
⑤.为避免连续扩容消耗内存,能初始化容量大小尽量指定容量
⑥.为啥会非线程安全,因为方法内部并非原子操作
参考:
https://zhuanlan.zhihu.com/p/72296421 hashmap
https://zhuanlan.zhihu.com/p/73283922 linkedhashmap
https://zhuanlan.zhihu.com/p/72463637 hashset
https://zhuanlan.zhihu.com/p/72156592 arraylist
https://www.jianshu.com/p/f174d49b391c
https://www.cnblogs.com/LiaHon/p/11089988.html arraylist
https://blog.csdn.net/u012859681/article/details/78206494 线程安全问题
从JDK源码学习Arraylist的更多相关文章
- 由JDK源码学习ArrayList
ArrayList是实现了List接口的动态数组.与java中的数组相比,它的容量能动态增长.ArrayList的三大特点: ① 底层采用数组结构 ② 有序 ③ 非同步 下面我们从ArrayList的 ...
- JDK源码学习系列04----ArrayList
JDK源码学习系列04----ArrayList 1. ...
- JDK源码学习--String篇(二) 关于String采用final修饰的思考
JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...
- JDK源码学习系列05----LinkedList
JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实 ...
- JDK源码学习系列03----StringBuffer+StringBuilder
JDK源码学习系列03----StringBuffer+StringBuilder 由于前面学习了StringBuffer和StringBuilder的父类A ...
- JDK源码学习系列02----AbstractStringBuilder
JDK源码学习系列02----AbstractStringBuilder 因为看StringBuffer 和 StringBuilder 的源码时发现两者都继承了AbstractStringBuil ...
- JDK源码学习系列01----String
JDK源码学习系列01----String 写在最前面: 这是我JDK源码学习系列的第一篇博文,我知道 ...
- JDK源码学习笔记——LinkedHashMap
HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...
- JDK1.8源码学习-ArrayList
JDK1.8源码学习-ArrayList 目录 一.ArrayList简介 为了弥补普通数组无法自动扩容的不足,Java提供了集合类,其中ArrayList对数组进行了封装,使其可以自动的扩容或缩小长 ...
随机推荐
- (27)ASP.NET Core .NET标准REST库Refit
1.简介 Refit是一个受到Square的Retrofit库(Java)启发的自动类型安全REST库.通过HttpClient网络请求(POST,GET,PUT,DELETE等封装)把REST AP ...
- HashSet底层、及存入对象时候如何保持唯一
HashSet底层.及存入对象时候如何保持唯一 在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里. 但是当位于一个桶中的元素较多,即hash ...
- 迄今为止最硬核的「Java8时间系统」设计原理与使用方法
为了使本篇文章更容易让读者读懂,我特意写了上一篇<任何人都需要知道的「世界时间系统」构成原理,尤其开发人员>的科普文章.本文才是重点,绝对要读,走起! Java平台时间系统的设计方案 几乎 ...
- 看逐浪CMS技术小哥做SVG动画(附使用Bodymovin和Lottie将Adobe After Effects(AE)程式转为 HTML5/Android/iOS原生的动画全过程-即AE转svg\canvas\html5动画)
名词解解释 adobe After Effects AE:adobe After Effects,adobe公司的专业视频制作软件. Bodymovin插件预览 Bodymovin:是一个AE的插 ...
- Go组件学习:如何读取ini配置文件
代码示例全部保存在,欢迎star:https://github.com/EnochZg/golang-examples 安装组件 go get gopkg.in/ini.v1 使用 先创建ini后缀的 ...
- 开发RTSP 直播软件 H264 AAC 编码
上一篇对摄像头预览,拍照做了大概的介绍,现在已经可以拿到视频帧了,在加上 RTSP 实现,就是直播的雏形,当然还要加上一些 WEB 管理和手机平台的支援,就是一整套直播软件. 介绍一些基础概念:RTP ...
- java面试汇总一
第一部分 Java SE基础(1) 1.1 java的8种基本数据类型 装箱 拆箱 1.1.1 8种基本的数据类型 1.1.2装箱 拆箱 自动装箱是 Java 编译器在基本数据类型和对应的对象包 ...
- Django的路由系统01-路由分发
1. Including other URLconfs 原urls.py文件,多个app的路由系统写在一起,不方便管理 范例: from django.conf.urls import url fro ...
- Uniapp使用GoEasy实现websocket实时通讯
Uniapp作为近来最火的移动端开发技术,一套代码,可以打包成Android/iOS app和各种平台的小程序,可谓是没有最方便只有更方便. GoEasy上架DCloud Uniapp插件市场已经有一 ...
- CVE-2013-4710 WebView addJavascriptInterface远程执行代码
WebView是Android平台下的一个重要组件,通常用来在Activity中嵌入一个简单的浏览器,实现在线网页浏览的功能.比如下面代码实现访问Google页面: WebView webView = ...