ArrayList源码学习
来源:互联网 发布:mac系统如何下载软件 编辑:程序博客网 时间:2024/05/20 04:11
ArrayList类
ArrayList
继承自:AbstractList
实现接口: List, RandomAccess, Cloneable, java.io.Serializable
属性
//实现Serializable接口,生成的序列版本号
private static final long serialVersionUID = 8683452581122892189L;
//默认初始化容量
private static final int DEFAULT_CAPACITY = 10;
两个空数组对象
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
你一定很好奇为什么弄两个??除了名字其他完全一样啊,有必要吗?
先看官方解释:
We distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when first element is added.
用来区分当第一次添加元素时,需要扩容的大小。之前自己没注意到这个细节,后边在 add() 操作中判断是否扩容时会有分析。
//ArrayList中实际存储元素的数组,看到这里是不是感觉我写错了?我也搞不懂为什么 elementData 属性设置默认访问权限,莫非是因为有 transient 不序列化的原因?
transient Object[] elementData;
//最大数组容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//记录当前ArrayList的长度,而不是 elementData 数组的长度,elementData数组会有值为 null 的位置,而且可能会很多。下面也会说到
private int size;
构造方法
当使用无参构造方法时,给 elementData 数组设置的是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
使用有参构造器且传入的值是0时,为 elementData数组设为 EMPTY_ELEMENTDATA
这里也没有本质区别,都是空嘛,下边的扩容操作才是亮点!
//注意没有一个构造方法设置 size 值public ArrayList(int initialCapacity) {//指定初始化容量 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA;// } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }}public ArrayList() {//使用默认初始化容量 10 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
add(E e)
ArrayList 在每次新增元素时,都会首先进行判断是否需要动态扩容。
注意上边的两个构造方法,分两类
1. 当使用无参构造器时,把 elementData 数组设置为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA终于派上用场了!在这种情况下进行「首次」 add() 操作时,会一次性把 elementData 数组大小设为 10 ,到这里扩容完毕。之后的第 2~9 次 add() 操作不会扩容,因为在判断是否需要扩容即执行ensureExplicitCapacity(minCapacity)函数时,传入的值是3-10,都小于等于执行完第一次 add() 操作后的数组大小10。只有在第十次 add() 操作时才会扩容,此时会扩容为原数组长度的 1.5倍。再之后再进行 add() 操作,会把当前 ArrayList 中的元素数量加一(size+1)和 elementData数组大小比较,若前者大,则继续扩容为当前数组长度的 1.5倍……
2. 使用有参构造器时,当进行 add() 时,只是把 size+1和 elementData.length()进行比较,若前置大就扩容至 1.5倍。
最后把 elementData[size]设置为 传入的值 e,并执行 size++操作。
public boolean add(E e) { //每次添加都会先进行判断 ensureCapacityInternal(size + 1); elementData[size++] = e; return true;}private void ensureCapacityInternal(int minCapacity) {//使用无参构造器且首次添加元素时,设置最小扩容大小为10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //默认初始化容量10 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //判断是否需要扩容 ensureExplicitCapacity(minCapacity);}private void ensureExplicitCapacity(int minCapacity) { //操作数+1 modCount++;//(**继承自AbstractList) //判断 最小扩容容量>数组大小 : if (minCapacity - elementData.length > 0) //扩容: grow(minCapacity);}//扩大至原来的1.5倍或者当前 size+1 ,谁大要谁private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
使用无参构造器和传入值为0的有参构造器扩容的「区别」
前者是在首次 add() 时,会一下子把 elementData 数组的长度扩大到10,之后在第10次操作时才会扩容。
而后者在执行 add()时,第一次时会扩容为 1,elementData[0]=e;
第二次仍然会扩容至2,elementData[1]=e;
第三次仍然会扩容至3,elementData[2]=e;
第四次仍然会扩容至4,elementData[3]=e;
第五次仍然会扩容至6,elementData[4]=e;
……
扩容次数在前面几次会明显增加
remove(int index)
public E remove(int index) { //检查角标是否合法:不合法抛异常 rangeCheck(index); //操作数+1: modCount++; //获取当前角标的value: E oldValue = elementData(index); //获取需要删除元素 到最后一个元素的长度,也就是删除元素后,后续元素移动的个数; int numMoved = size - index - 1; //如果移动元素个数大于0 ,也就是说删除的不是最后一个元素: if (numMoved > 0) // 将elementData数组index+1位置开始拷贝到elementData从index开始的空间 System.arraycopy(elementData, index+1, elementData, index, numMoved); //size减1,并将最后一个元素置为null elementData[--size] = null; //返回被删除的元素: return oldValue;}
- java源码学习-----ArrayList
- java ArrayList源码学习
- JAVA源码学习-ArrayList
- 源码学习之ArrayList
- java源码学习----ArrayList
- ArrayList源码学习
- ArrayList源码学习笔记
- ArrayList源码学习
- ArrayList源码学习
- ArrayList源码学习
- ArrayList源码学习
- JDK源码学习之ArrayList
- 集合学习--ArrayList 源码初探
- ArrayList<E>源码学习笔记
- ArrayList部分源码学习笔记
- ArrayList源码解析与学习
- jdk源码学习笔记---ArrayList
- ArrayList源码学习(1.1)
- kotlin学习笔记3
- 顺时针打印矩阵
- LintCode 647 Substring Anagrams
- Linux常用命令(1)
- Python IO笔记
- ArrayList源码学习
- 230. Kth Smallest Element in a BST
- 计算机组成原理与系统结构 第一章计算机概述
- Qt5开发学习之事件(十三)
- 《C++ Concurrency in Action》笔记4 hardware_concurrency()
- Linux系统下FTP服务器的搭建
- 九度题目1156:谁是你的潜在朋友
- android实现和web一样的圆角
- 无限轮播+GridView