从今天开始从源码去学习一些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的更多相关文章

  1. 由JDK源码学习ArrayList

    ArrayList是实现了List接口的动态数组.与java中的数组相比,它的容量能动态增长.ArrayList的三大特点: ① 底层采用数组结构 ② 有序 ③ 非同步 下面我们从ArrayList的 ...

  2. JDK源码学习系列04----ArrayList

                                                                             JDK源码学习系列04----ArrayList 1. ...

  3. JDK源码学习--String篇(二) 关于String采用final修饰的思考

    JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...

  4. JDK源码学习系列05----LinkedList

                                             JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实 ...

  5. JDK源码学习系列03----StringBuffer+StringBuilder

                         JDK源码学习系列03----StringBuffer+StringBuilder 由于前面学习了StringBuffer和StringBuilder的父类A ...

  6. JDK源码学习系列02----AbstractStringBuilder

     JDK源码学习系列02----AbstractStringBuilder 因为看StringBuffer 和 StringBuilder 的源码时发现两者都继承了AbstractStringBuil ...

  7. JDK源码学习系列01----String

                                                     JDK源码学习系列01----String 写在最前面: 这是我JDK源码学习系列的第一篇博文,我知道 ...

  8. JDK源码学习笔记——LinkedHashMap

    HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序. LinkedHashMap保证了元素迭代的顺序.该迭代顺序可以是插入顺序或者是访问顺序.通过维护一个 ...

  9. JDK1.8源码学习-ArrayList

    JDK1.8源码学习-ArrayList 目录 一.ArrayList简介 为了弥补普通数组无法自动扩容的不足,Java提供了集合类,其中ArrayList对数组进行了封装,使其可以自动的扩容或缩小长 ...

随机推荐

  1. bootstrap table分页跳转到第一页

    1.destroy后重新初使化表格,可以将表格初始化封装为一个函数,destory后重新调用该函数进行初始化: 2.使用url刷新表格,$('#table').bootstrapTable('refr ...

  2. Unsafe中CAS的实现

    前言 Unsafe 是位于 sun.misc 包下的一个类.Unsafe 提供的 API 大致可分为内存操作.CAS.Class 相关.对象操作.线程调度.系统信息获取.内存屏障.数组操作等几类.由于 ...

  3. Simulink仿真入门到精通(十二) Publish发布M文件

    12.1 M文件的注释 使用%进行注释. 连续多行注释Ctrl+R,取消注释Ctrl+T. 12.2 Cell模式 在MATLAB脚本文件中使用连续两个注释符,开启一个新的Cell块,%%后空一格追加 ...

  4. 【5min+】 一个令牌走天下!.Net Core中的ChangeToken

    系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...

  5. Navicat15最新版本破解 亲测可用!!!

    1.下载Navicat Premium官网https://www.navicat.com.cn/下载最新版本下载安装 2.本人网盘链接:https://pan.baidu.com/s/1ncSaxId ...

  6. 常用计算机CMD 命令

      cd\  '返回到根目录  cd..  '返回到上一级目录 1 cd 显示当前目录名或改变当前目录. 2 dir 显示目录中的文件和子目录列表.  3 md 创建一个目录.  4 mkdir  创 ...

  7. Vue 学习笔记(四)

    一.路由简单示例 HTML <script src="https://unpkg.com/vue/dist/vue.js"></script> <sc ...

  8. oracle去除重复数据与oracle分页

    一.去除oracle中重复数据,可以使用rowid列,rowid列是一个伪列,该列在数据库中灭一个表中都有,但是我们查询数据库的时候,默认都没有给我们返回这一列,这一列用来区分数据库中的每一行时间,可 ...

  9. 一些http知识

    一些http知识 前置知识 首先放一张非常经典的计算机网络 关于五层结构 「物理层」「数据链路层」「网络层」「运输层」「应用层」我们依次来讲 我们拿wireshark 抓个http包看一下就能明白各层 ...

  10. AspNetCore3.1_Secutiry源码解析_4_Authentication_JwtBear

    title: "AspNetCore3.1_Secutiry源码解析_4_Authentication_JwtBear" date: 2020-03-22T16:29:29+08: ...