数组在创建的时候长度是固定的,那么就有往ArrayList中不断添加对象的时候,那么ArrayList是如何管理这些数组的?

ArrayList内部通过Object[]实现,我们通过分析ArrayList的构造和add和remove和clear方法来分析

一、构造函数
1)空参构造

  1. /**
  2. * Constructs a new {@code ArrayList} instance with zero initial capacity.
  3. */
  4. public ArrayList() {
  5. array = EmptyArray.OBJECT;
  6. }

array是一个Object[]类型。当我们new一个空参构造时系统调用了EmptyArray.OBJECT属性,EmptyArray仅
仅是一个系统的类库,该类源码如下:

  1. public final class EmptyArray {
  2. private EmptyArray() {}
  3.  
  4. public static final boolean[] BOOLEAN = new boolean[0];
  5. public static final byte[] BYTE = new byte[0];
  6. public static final char[] CHAR = new char[0];
  7. public static final double[] DOUBLE = new double[0];
  8. public static final int[] INT = new int[0];
  9. public static final Class<?>[] CLASS = new Class[0];
  10. public static final Object[] OBJECT = new Object[0];
  11. public static final String[] STRING = new String[0];
  12. public static final Throwable[] THROWABLE = new Throwable[0];
  13. public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0]; }

也就是说当我们new 一个空参ArrayList的时候,系统内部使用了一个new Object[0]数组。

2)带参构造1

  1. /**
  2. * Constructs a new instance of {@code ArrayList} with the specified
  3. * initial capacity.
  4. *
  5. * @param capacity
  6. * the initial capacity of this {@code ArrayList}.
  7. */
  8. public ArrayList(int capacity) {
  9. if (capacity < 0) {
  10. throw new IllegalArgumentException("capacity < 0: " + capacity);
  11. }
  12. array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
  13. }

该构造函数传入一个int值,该值作为数组的长度值。如果该值小于0,则抛出一个运行时异常。如果等于0,则
使用一个空数组,如果大于0,则创建一个长度为该值的新数组。

3)带参构造2

  1. /**
  2. * Constructs a new instance of {@code ArrayList} containing the elements of
  3. * the specified collection.
  4. *
  5. * @param collection
  6. * the collection of elements to add.
  7. */
  8. public ArrayList(Collection<? extends E> collection) {
  9. if (collection == null) {
  10. throw new NullPointerException("collection == null");
  11. }
  12.  
  13. Object[] a = collection.toArray();
  14. if (a.getClass() != Object[].class) {
  15. Object[] newArray = new Object[a.length];
  16. System.arraycopy(a, 0, newArray, 0, a.length);
  17. a = newArray;
  18. }
  19. array = a;
  20. size = a.length;
  21. }  

若调用构造函数的时候传入了一个Collection的子类,那么先判断集合是否为null,为null则抛出空指针异常.如果不是则将集合转换为数组a,然后将该数组赋值为成员变量array,将该数组的长度作为成员变量size.这里面它先判断a.getClass是否等于Object[].class,一般都是相等的,判断是为了增加安全性,toArray方法是Collection接口定义的,因此其所有的子类都是这样的方法,list集合的toArray和Set集合的toArray返回的都是Object[]数组.

二、add方法
add方法有两个重载,这里只研究最简单的那个.

  1. /**
  2. * Adds the specified object at the end of this {@code ArrayList}.
  3. *
  4. * @param object
  5. *
  6. the object to add.
  7. * @return always true
  8. */
  9. @Override public boolean add(E object) {
  10. Object[] a = array;
  11. int s = size;
  12. if (s == a.length) {
  13. Object[] newArray = new Object[s +
  14. (s < (MIN_CAPACITY_INCREMENT / 2) ?
  15. MIN_CAPACITY_INCREMENT : s >> 1)];
  16. System.arraycopy(a, 0, newArray, 0, s);
  17. array = a = newArray;
  18. }
  19. a[s] = object;
  20. size = s + 1;
  21. modCount++;
  22. return true;
  23. }

1、首先将成员变量array赋值给局部变量a,将成员变量size赋值给局部变量s。
2、判断集合的长度s是否等于数组的长度(如果集合的长度已经等于数组的长度了,说明数组已经满了,该重新
分配新数组了),重新分配数组的时候需要计算新分配内存的空间大小,如果当前的长度小于
MIN_CAPACITY_INCREMENT/2(这个常量值是12,除以2就是 6,也就是如果当前集合长度小于6)则分配12个
长度,如果集合长度大于6则分配当前长度s的一半长度。这里面用到了三元运算符和位运算,s >> 1,意思就是将
s往右移1位,相当于s=s/2,只不过位运算是效率最高的运算。
3、将新添加的object对象作为数组的a[s]个元素。
4、修改集合长度size为s+1
5、modCotun++,该变量是父类中声明的,用于记录集合修改的次数,记录集合修改的次数是为了防止在用迭代
器迭代集合时避免并发修改异常,或者说用于判断是否出现并发修改异常的。
6、return true,这个返回值意义不大,因为一直返回true,除非报了一个运行时异常。

三、remove方法
remove方法有两个重载,这里只研究remove(int index)方法。

  1. /**
  2. * Removes the object at the specified location from this list.
  3. *
  4. * @param index
  5. * the index of the object to remove.
  6. * @return the removed object.
  7. * @throws IndexOutOfBoundsException
  8. * when {@code location < 0 || location >= size()}
  9. */
  10. @Override public E remove(int index) {
  11. Object[] a = array;
  12. int s = size;
  13. if (index >= s) {
  14. throwIndexOutOfBoundsException(index, s);
  15. }
  16. @SuppressWarnings("unchecked")
  17. E result = (E) a[index];
  18. System.arraycopy(a, index + 1, a, index, --s - index);
  19. a[s] = null; // Prevent memory leak
  20. size = s;
  21. modCount++;
  22. return result;
  23. }

1、先将成员变量array和size赋值给局部变量a和s。
2、判断形参index是否大于等于集合的长度,如果成了则抛出运行时异常
3、获取数组中脚标为index的对象result,该对象作为方法的返回值
4、调用System的arraycopy函数

5、接下来就是很重要的一个工作,因为删除了一个元素,而且集合整体向前移动了一位,因此需要将集合最后一个元素设置为null,否则就可能内存泄露。 

6、重新给成员变量array和size赋值
7、记录修改次数
8、返回删除的元素

四、clear方法

  1. /**
  2. * Removes all elements from this {@code ArrayList}, leaving it empty.
  3. *
  4. * @see #isEmpty
  5. * @see #size
  6. */
  7. @Override public void clear() {
  8. if (size != 0) {
  9. Arrays.fill(array, 0, size, null);
  10. size = 0;
  11. modCount++;
  12. }
  13. }

如果集合长度不等于0,则将所有数组的值都设置为null,然后将成员变量size 设置为0即可,最后让修改记录加1.

  

  

 

ArrayList内部实现原理的更多相关文章

  1. Java集合:ArrayList的实现原理

    Java集合---ArrayList的实现原理   目录: 一. ArrayList概述 二. ArrayList的实现 1) 私有属性 2) 构造方法 3) 元素存储 4) 元素读取 5) 元素删除 ...

  2. Mininet的内部实现原理简介

    原文发表在我的博客主页,转载请注明出处. 前言 之前模拟仿真网络一直用的是Mininet,包括写了一些关于Mininet安装,和真实网络相连接,Mininet简历拓扑的博客,但是大多数都是局限于具体步 ...

  3. KVO内部实现原理

    KVO的原理: 只要给一个对象注册一个监听, 那么在运行时, 系统就会自动给该对象生成一个子类对象, (格式如:NSKVONotifying_className), 并且重写自动生成的子类对象的被监听 ...

  4. Java基础知识强化之集合框架笔记23:ArrayList的实现原理

    1. ArrayList的实现原理: 这个可以直接参考网友的博客:http://www.cnblogs.com/ITtangtang/p/3948555.html

  5. Angular单页应用&AngularJS内部实现原理

    回顾 自定义指令 登录后获取登录信息session 首先在登录验证的时候保存一个user 在学生管理页面中运用ajax调用获取到登录的用户信息 对注销按钮添加点击事件:调用ajax在表现层给user赋 ...

  6. 8. 理解ZooKeeper的内部工作原理

    到目前为止,我们已经讨论了ZooKeeper服务的基础知识,并详细了解了数据模型及其属性. 我们也熟悉了ZooKeeper 监视(watch)的概念,监视就是在ZooKeeper命名空间中的znode ...

  7. ArrayList的实现原理

    ArrayList的线性复杂度是1.想确定一个数据,直接通过索引进行访问.实际上这个过程和数组是非常相似的.ArrayList在整个使用过程中,如果想要高效操作,最好设置一个数组的大小.在个数固定的情 ...

  8. Redis有序集内部实现原理分析(二)

    Redis技术交流群481804090 Redis:https://github.com/zwjlpeng/Redis_Deep_Read 本篇博文紧随上篇Redis有序集内部实现原理分析,在这篇博文 ...

  9. 简单复习一下ArrayList的扩容原理

    刚刚跟几个好朋友喝完小酒回家,简单大概复习一下ArrayList的扩容原理,由于头有点小晕,就只大概说一下扩容的原理哈: 首先ArrayList实现了List接口,继承了AbstractList,大家 ...

随机推荐

  1. js由浅入深理解

    隐式转换 + - num - 0 把num转换成number: num + "" 把num转换成字符串: ------------------------------------- ...

  2. how to make a function from using global var to not using it

    let say, we want to find the bottom left node in a tree.one way to do it is using global vars: /** * ...

  3. this.$emit('on-select-change' emit里面不能写大写字母

    this.$emit('on-select-change' emit里面不能写大写字母 刚试了下 也能写大写 但是 两边就都写一样就完了,就都写成带-的就完了

  4. slides 在线ppt && React && Angular

    现在主流前端框架 有3个 Vue React Angular 如果有时间就都学习,理解一下他们的差异性~ 在线ppt的一个网站 这个是npm讲解的,不错 https://slides.com/seld ...

  5. 按名字寻找文件和文件夹 find命令

    find <指定目录> <指定条件> <指定动作> find /home/bnrc/py-faster-rcnn/caffe-fast-rcnn/ -name 'd ...

  6. 区间DP || HDU 6249 Alice’s Stamps

    题意:标号为1-n的n种邮票,m个邮票集,每个集里有标号从Li到Ri的邮票,要从中选K个邮票集,使这K个邮票集能覆盖最多种的邮票,问最多能覆盖多少种邮票 思路:区间DP (我:??? f[i][j]表 ...

  7. RestTemplate Hashmap变为LinkedHashMap源码解读

    使用restTemplate远程调用服务,正常应该接收List<HashMap>数据,但实际却是List<LikedHashMap>经过不断地debug,终于找到了数据被转换成 ...

  8. vue 父子组件的加载顺序

    一.加载渲染过程 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount ...

  9. 6 SQL 函数、谓词、CASE表达式

    6 函数.谓词.CASE表达式 6-1 各种各样的函数 /* 所谓函数,就是输入某一值得到相应输出结果的功能.输入值称为参数(parameter),输出值称为返回值. 函数大致可以分为以下几种 : 算 ...

  10. Tomcat server.xml配置文件

    server.xml配置文件: <?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to ...