ArrayList源码分析
来源:互联网 发布:vb里头mid函数查找 编辑:程序博客网 时间:2024/06/01 09:15
ArrayList
概述
ArrayList底层通过数组的方式来实现List接口,size、isEmpty、get、set等操作都是O(1)时间复杂度,而add是均摊常数时间复杂度(amortized constant time,可以理解为扩容的频率不太高,主要关注统计情况下的复杂度,因此均摊到所有操作上就是常数时间)。
size表示元素的数目,capacity表示当前数组的容量,ArrayList在添加元素时会进行判断,若数目超过了容量会进行自动扩容。
该容器不是线程安全的,可通过Collections.synchronizedList()方法来转化为线程安全容器,这应该在创建的时候完成。
iterator返回的迭代器是fail-fast的,就是说如果返回迭代器后,用除了ListIterator的remove和add的其他方法进行修改时,会抛出ConcurrentModificationException,这主要是为了避免一些随机的风险。
源码分析
- 构造器
主要的两个构造器如下:
// 创建一个默认容量的空列表 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }// 创建一个给定容量的空列表 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); } }
逻辑很简单,没有参数时是构造一个空数组,传入一个capacity时也只是判断一下数值得范围做检查而已。
EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个static final的空数组,其实就是用作一个共享的状态,既然都是空数组,为什么有两个?区别在于add第一个元素时的扩容机制不同,后面分析add时会看到。
- add
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }// 保证内部数组的容量能够容纳元素,必要时进行扩容private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// DEFAULT_CAPACITY为10,所以如果构造时没传容量,是默认的空列表,第一次添加元素时minCapacity为max(10,1)=10;如果是容量为0的空列表,这句不会执行。 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }private void ensureExplicitCapacity(int minCapacity) {// 增加修改次数 modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0)// 如果所需的容量比当前容量大,则进行扩容 grow(minCapacity); }// 这个方法包含扩容的逻辑private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // 可以看到,扩容后的大小为原来容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
主要的逻辑都注释了,grow方法就是扩容的代码,主要就是扩充为1.5倍,如果没有溢出就用Arrays.copyOf复制到新的数组,可见这种方式复制数组是比较快的。
3. remove
// 删除索引上的元素。删除元素后,index后面的元素都会向前移动 public E remove(int index) { // 检查范围的合法性 rangeCheck(index); modCount++; E oldValue = elementData(index); // 需要移动的元素数 int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); // 注意到这一行,手动设为null是为了防止内存泄漏 elementData[--size] = null; // clear to let GC do its work return oldValue; } // 删除数组中第一个对应的元素 public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { // fastRemove的逻辑其实与上一个remove中的逻辑基本一样,移动后面元素,只是少了边界检查。 fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
整体逻辑也比较简单,检查了范围后用System.arraycopy来移动元素,并将最后一个元素设为null防止内存泄漏。
另外也可以看到,删除元素时是不会缩小数组的容量的,如果要节省空间,可以使用trimToSize来收缩数组.
4. get和set
public E get(int index) { rangeCheck(index); return elementData(index); } public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
这两个方法就很简单了,检查一下范围就直接进行数组的对应操作。
总结
总的来说ArrayList比较简单,主要要知道底层是由数组实现的列表,非线程安全,了解其扩容机制,使用时就心中有数了。
ArrayList随机查找是常数时间,而随机增删元素时是线性时间,适合查找较多、修改较少的顺序存储的场景。
- ArrayList源码分析
- ArrayList源码分析
- ArrayList 源码分析
- ArrayList源码分析
- ArrayList LinkedList 源码分析
- ArrayList,LinkedList源码分析
- 源码分析之ArrayList
- ArrayList 源码分析
- ArrayList源码分析
- ArrayList源码分析
- ArrayList源码分析
- ArrayList源码分析
- ArrayList源码分析
- ArrayList 源码分析
- Java ArrayList源码分析
- ArrayList源码分析
- Java ArrayList 源码分析
- ArrayList的源码分析
- 一次 Spark SQL 性能提升10倍的经历
- jQuery添加options点击事件并传值实例代码
- android获取设备中所有应用的包名
- node.js之定时任务+文件读取+INSERTALL
- 【C++】C++常见面试题汇总_持续更新中...
- ArrayList源码分析
- 基于OpenSSL颁发数字证书的一个完整实例
- rosen老师 的gitconfig配置
- python3爬虫伪装代理IP
- 老僧长谈设计模式-7-代理模式2
- 游戏中的小地图
- spring boot实战(第三篇)事件监听源码分析
- 华为打开日志方法
- LinkedList源码分析