一、 ArrayList

class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • ​ ArrayList是基于数组实现的,是一个动态数组,其容量能够自动增长

  • ​ ArrayList是线程不安全的

  • ​ 实现了RandomAccess接口,所以支持快速访问,

  • ​ 实现了Cloneable 接口, 能够被克隆

  • ​ 实现了Serializable接口,支持序列化

二、 ArrayList的构造函数

 	private static final long serialVersionUID = 8683452581122892189L;
/**
* 默认初始容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 用于空实例的共享空数组实例
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
*用于默认大小的空实例的共享空数组实例。我们将其与EMPTY_ELEMENTDATA区分开来,以了解何时 充气多少添加第一个元素。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; / * *
* 存储ArrayList元素的数组缓冲区。
* ArrayList的容量是这个数组缓冲区的长度。任何
*使用elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList
*将在添加第一个元素时扩展为DEFAULT_CAPACITY,也就是10。
* /
transient Object[] elementData; private int size; /**
*默认构造函数,使用初始容量10构造一个空列表(无参数构造)
*/
public ArrayList() {
//上面的Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
} /**
* 带初始容量参数的构造函数。(用户自己指定容量)
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {//初始容量大于0
//创建initialCapacity大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {//初始容量等于0
//创建空数组 上面的 Object[] EMPTY_ELEMENTDATA = {};
this.elementData = EMPTY_ELEMENTDATA;
} else {//初始容量小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
} /**
*构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
*如果指定的集合为null,throws NullPointerException。
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}

​ 仔细观察的话可以发现在以无参构造方法或者有参构造但是参数默认为0时创建的ArrayList,实际上初始化赋值的是一个空数组。

三、 进一步分析ArrayList的add()

​ 方便起见,这里以无参构造函数创建的ArrayList为例来分析

​ 1、先看下Add()方法

    /**
* 将指定的元素追加到此列表的末尾.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

​ 2、 这里面用到了ensureCapacityInternal()方法

​ add方法里面调用了 ensureCapacityInternal(size + 1),当添加的是第一个元素的时候(size + 1 == 1);Math.max(DEFAULT_CAPACITY, minCapacity)里面获取到的最大值是10,

   // 获取要扩大的容量
private void ensureCapacityInternal(int minCapacity) {
//上面默认的是 Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// DEFAULT_CAPACITY = 10, 获取两者中的最大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
}

​ 3、 接着又调用了ensureExplicitCapacity()方法

 // 判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code 有溢出的代码
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

先来分析一波:

​ 当我们添加第一个元素的时候,elementData.length是等于0的(因为默认它是一个空的list),之后会执行ensureCapacityInternal()方法,此时minCapacity大小是10,minCapacity - elementData.length > 0 这个条件成立。接着就会调用grow(minCapacity) 方法。

​ 但是当我们添加第二个元素的时候,elementData.length在添加第一个元素之后就变成了10,minCapacity - elementData.length > 0 判断不通过,所以不会执行grow(minCapacity) 方法

​ 当添加到第11个元素的时候 11 >10 所以会继续调用grow(minCapacity) 方法。

4、里面又用到了grow(minCapacity) 方法

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
* 增加容量,确保他至少能够容纳最小容量参数指定的元素,
* 初次添加元素的时候minCapacity 是等于10 的
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// >> 右移,右移一位,相当于除以2,oldCapacity >> 1 = oldCapacity/2
// 将原有容量扩大为原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//比较新容量和最小容量,如果新容量小于最小容量,将最小容量赋值为新容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; // 如果新容量大于MAX_ARRAY_SIZE,就执行hugeCapacity 方法
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);
}

​ 当添加第一个元素的时候,oldCapacity为0 ,经过判断第一个条件成立,所以newCapacity 现在等于10。但是第二个if条件判断不成立,所以不会进入hugeCapacity 方法。

​ 当添加第11个元素的时候,oldCapacity为10,newCapacity为15 大于minCapacity,所以第一个条件判断不成立。第二个if条件判断也不成立,所以不会进入到hugeCapacity 方法。

5、hugeCapacity 方法

   private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE:MAX_ARRAY_SIZE;
}

四、 ArrayList.add(int index, E element)

​ 在指定位置添加元素需要先对index进行边界检查,查看它是否越界,之后调用ensureCapacityInternal来确保capacity足够大。再将从index开始之后的所有元素后移一位,将element插入到index位置,最后size加1.

    /**
* 将指定元素插入到list中的指定位置
*/
public void add(int index, E element) {
rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!!
//arraycopy()方法实现数组自己复制自己
//elementData:源数组;index:源数组中的起始位置;elementData:目标数组;index +1:目标数组中的起始位置; size - index:要复制的数组元素的数量;
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}

里面调用了rangeCheckForAdd方法

   // 判断当前索引是否越界
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

jdk8下面的ArrayList的扩容的更多相关文章

  1. 浅谈 ArrayList 及其扩容机制

    浅谈ArrayList ArrayList类又称动态数组,同时实现了Collection和List接口,其内部数据结构由数组实现,因此可对容器内元素实现快速随机访问.但因为ArrayList中插入或删 ...

  2. Arraylist动态扩容详解

    ArrayList 概述 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长. ArrayList不是线程安全的,只能用在单线程环境下. 实现了Serializable接口,因此它支 ...

  3. ArrayList增加扩容问题 源码分析

    public class ArrayList<E>{ private static final int DEFAULT_CAPACITY = 10;//默认的容量是10 private s ...

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

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

  5. JDK8下的HashMap有什么特别之处?

    一.前言 上篇认真的分析了在JDK7下的HashMap, 如果还没看过的或者忘记了的可以先去回顾下,这样可以更好的了解JDK8下的HashMap基于JDK7做了什么改动.分析JDK8下的HashMap ...

  6. 关于ArrayList的扩容机制

    关于ArrayList的扩容机制 ArrayList作为List接口常用的一个实现类,其底层数据接口由数组实现,可以保证O(1) 复杂度的随机查找, 在增删效率上不如LinkedList,但是在查询效 ...

  7. JVM源码分析之JDK8下的僵尸(无法回收)类加载器[z]

    [z]http://lovestblog.cn/blog/2016/04/24/classloader-unload/ 概述 这篇文章基于最近在排查的一个问题,花了我们团队不少时间来排查这个问题,现象 ...

  8. 在不损坏C盘的情况下为C盘扩容,适用于Win

    2016年12月29日14:29:27 参考原文:http://jingyan.baidu.com/article/90808022a6c6b7fd91c80fc8.html 在不损坏磁盘的情况下给某 ...

  9. eclipse 下面的folder,source folder,package的区别与作用

    首先明确一点,folder,source folder,package都是文件夹,既然是文件夹,那么任何的文件都可以往这三种文件夹下面的放.1.他们的区别folder就是普通的文件夹,它和我们wind ...

随机推荐

  1. Android 神兵利器之通过解析网页获取到的API数据合集,可拿来就用

    AppApis 前段时间,写了个做app的实战系列教程,其中一篇章提到了解析网页中的数据为己所用,看到大家的响应还不错,于是把自己以前解析过的网页数据都整理了下,开放出来,给更多的人使用,希望可以帮助 ...

  2. 2019-8-29-dotnet-core-使用-sqlite-部署到-Centos-服务器

    title author date CreateTime categories dotnet core 使用 sqlite 部署到 Centos 服务器 lindexi 2019-08-29 19:1 ...

  3. Java面向对象----继承概念,super关键字

    继承概念: 继承需要符合的关系  is-a  , 父类通用更抽象,子类更特殊更具体 类之间的关系 继承体现 组合体现 实现接口体现 继承的意义 代码重用 体现不同抽象层次 extends关键字 Sup ...

  4. Mybatis表关联多对多

    创建表 创建表对应的 JavaBean 对象 package com.tanlei.newer.model; import java.util.List; /** * @author:Mr.Tan * ...

  5. Kubernetes1.3新特性:新的资源回收控制器

    (一)  核心概念 在kubernetes1.3中新增了一个资源回收控制器GarbaseCollector,用这个控制器来替代kubernetes1.3中的资源回收控制器GC. 如下为kubernet ...

  6. HDU 5572 An Easy Physics Problem【计算几何】

    计算几何的题做的真是少之又少. 之前wa以为是精度问题,后来发现是情况没有考虑全... 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5572 题意: ...

  7. 【牛腩视频】之SQL触发器 标签: 数据库 2015-05-23 09:44 1339人阅读 评论(40) 收藏

    之前在学习机房管理系统.net版的时候,已经写过了关于数据库的操作,但是现在开始学习牛腩,才发现之前自己理解的太浅显,很多东西看似好像会了,不去实际操作一下,不把代码从头到尾敲出来,到头来还是不会,所 ...

  8. PAN-OS 6.1 Open Source Software (OSS) Listing

    https://www.paloaltonetworks.com/documentation/oss-listings/oss-listings/pan-os-oss-listings/pan-os- ...

  9. 命名分组(?<name>....)

    捕获组分为: 普通捕获组(Expression) 命名捕获组(?Expression) 普通捕获组 从正则表达式左侧开始,每出现一个左括号"("记做一个分组,分组编号从 1 开始. ...

  10. Java反射机制(二):通过反射取得类的结构

    在反射运用过程中,如果你想得到一个类的完整结构,那么就要使用到java.lang.reflect包中的几个类: · Constructor  表示类中的构造方法 · Field  表示类中的属性 · ...