系统环境: JDK 1.7

成员变量

//默认的初始化数组大小
private static final int DEFAULT_CAPACITY = 10; //空的对象数组
private static final Object[] EMPTY_ELEMENTDATA = {}; //声明对象数组,不可序列化 transient
private transient Object[] elementData; //数组的大小
private int size;

构造方法

1.带有容量initialCapacity的构造方法

public ArrayList(int initialCapacity) {
super();
//参数小于0 则抛出异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
//new一个该大小的object数组赋给elementData
this.elementData = new Object[initialCapacity];
}

2.不带参数的构造方法

public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}

3.带参数Collection的构造方法

public ArrayList(Collection<? extends E> c) {
//将 c 转换为对象数据并赋值给elementData
elementData = c.toArray();
//获取数组大小
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} 注:c.toArray()返回的类型可能是非Object[]类型,如:Arrays.asList().toArray(),详情参考《关于 ArrayList.toArray()和Arrays.asList().toArray()方法》

总结:通过三个构造方法 我们发现ArrayList的实质就是封装了对数组的一些操作,通过这些操作,从而达到我们需要的目的

Add方法分析

public boolean add(E e) {
// 确保当前的数组大小可以装的下传入的对象e
ensureCapacityInternal(size + 1); // Increments modCount!!
// 在内部数组中存放对象,并将索引值+1
elementData[size++] = e;
return true;
} public void add(int index, E element) {
// 确保传入的数值没有越界
rangeCheckForAdd(index);
// 确保当前的数组大小可以装的下传入的对象e
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
} private void ensureCapacityInternal(int minCapacity) {
//针对new ArrayList()这种初始化方法有效,只有无参构造方法让内部数组等于empty。
if (elementData == EMPTY_ELEMENTDATA) {
// 确保 minCapacity 最小值是 10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//
ensureExplicitCapacity(minCapacity);
} //判断是不是真的需要扩容。
//如果我们用new ArrayList()初始化,根据上边的代码 minCapacity=10,而现在内部类真正的大小elementData.length是 0,所以需要扩容。
//如果我们用new ArrayList(20)初始化,内部数组在构造方法内已经初始化了,内部数组长度为20,而在add方法的时候,第一个操作传入的是size+1,即0+1。
//所以到达这个判断的时候,最小需要的容量为1,而长度为20 ,必然不需要扩容
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倍 整数右移1为 相当于除2
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);
}

Remove方法分析

  remove方法有2个,传入的参数分别是int和Object

public E remove(int index) {
//判断传入的数字是否在合理范围内,即是否小于数组内真实的数据个数
rangeCheck(index); modCount++;
E oldValue = elementData(index);//将要remove的索引位置的元素取出
//将内部数组中空出来的那个位置之后的元素移动到前边去
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将最后一位置空,size自减
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(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
} //移除数据操作
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
} //判断传入的数字是否在合理范围内
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
} @SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}

System.arraycopy()方法

*5个参数的含义分别是:
--src 源数组.
--srcPos 源数组中的起始位置。
--dest 目标数组。
--destPos 目标数据中的起始位置。
--length 要复制的数组元素的数目
int[] arr1 = { 0, 1, 2, 3, 4, 5 };
int[] arr2 = { 6, 7, 8, 9, 10, 11};
// 将arr1的元素复制到arr2中,从arr1的索引位置为3开始,复制长度为1个,到arr2中,arr2从索引为0的位置开始接受复制
System.arraycopy(arr1, 3, arr2, 0, 1);
// 所以最后结果是-- arr1:{ 0, 1, 2, 3, 4, 5 } arr2:{ 3, 7, 8, 9, 10, 11}
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));

整理自《http://blog.csdn.net/zw0283/article/details/51122255》

java(6) ArrayList源码的更多相关文章

  1. Java基础 ArrayList源码分析 JDK1.8

    一.概述 本篇文章记录通过阅读JDK1.8 ArrayList源码,结合自身理解分析其实现原理. ArrayList容器类的使用频率十分频繁,它具有以下特性: 其本质是一个数组,因此它是有序集合 通过 ...

  2. Java中ArrayList源码分析

    一.简介 ArrayList是一个数组队列,相当于动态数组.每个ArrayList实例都有自己的容量,该容量至少和所存储数据的个数一样大小,在每次添加数据时,它会使用ensureCapacity()保 ...

  3. Java集合ArrayList源码解读

    最近在回顾数据结构,想到JDK这样好的代码资源不利用有点可惜,这是第一篇,花了心思.篇幅有点长,希望想看的朋友认真看下去,提出宝贵的意见.  :) 内部原理 ArrayList 的3个字段 priva ...

  4. Java集合-ArrayList源码解析-JDK1.8

    ◆ ArrayList简介 ◆ ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAcc ...

  5. Java集合——ArrayList源码详解

    ) ArrayList 实现了RandomAccess, Cloneable, java.io.Serializable三个标记接口,表示它自身支持快速随机访问,克隆,序列化. public clas ...

  6. 【thinking in java】ArrayList源码分析

    简介 ArrayList底层是数组实现的,可以自增扩容的数组,此外它是非线程安全的,一般多用于单线程环境下(Vector是线程安全的,所以ArrayList 性能相对Vector 会好些) Array ...

  7. Java基础—ArrayList源码浅析

    注:以下源码均为JDK8的源码 一. 核心属性 基本属性如下: 核心的属性其实是红框中的两个: //从注释也容易看出,一个是集合元素,一个是集合长度(注意是逻辑长度,即元素的个数,而非数组长度) 其中 ...

  8. java.util.ArrayList源码分析

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess ...

  9. java的ArrayList源码摘要

    ArrayList本质上是一组对象数组,ArrayList有三种构造方法 1.指定长度创建ArrayList,2.默认长度为10创建.3,用旧的集合创建一个ArrayList. 对ArrayList的 ...

  10. Java集合-ArrayList源码分析

    目录 1.结构特性 2.构造函数 3.成员变量 4.常用的成员方法 5.底层数组扩容原理 6.序列化原理 7.集合元素排序 8.迭代器的实现 9.总结 1.结构特性 Java ArrayList类使用 ...

随机推荐

  1. 浅谈Linux系统中如何查看进程

    进程是一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源.一般来说,Linux系统会在进程之间共享程序代码和系统函数库,所以在任何时刻内存中都只有代码的一份拷贝. 1,ps命令 作用:p ...

  2. [转] fitnesse中的Map处理

    http://blog.csdn.net/doubeizhucele/article/details/42263887 fintesse会把!{}标记的变量视为HashTable对象,展现到页面上的将 ...

  3. 【PCA】

    http://blog.csdn.net/xiaojidan2011/article/details/11595869 非常清楚 核心部分解释:主成份用于降纬,通过线型变换,从高纬度映射到低纬度,其中 ...

  4. Ubuntu18.10下安装Qt5.12过程记录

    首先你得先安装Ubuntu操作系统(我是在VMWare14中安装的Ubuntu18.10版本). 阿里镜像:https://opsx.alibaba.com/mirror 我这里下载的文件为:ubun ...

  5. Cannot call sendError() after the response has been committed - baiyangliu

    当response提交后,不能调用sendError(),什么意思? 出现这个错误,一定是多次response导致的.可以这么理解,承载客户端和服务器进行Http交互的Socket连接已经关闭了,而你 ...

  6. Spring Cloud Sleuth 使用教程

    Spring Cloud Sleuth 使用案例 Span术语(Terminology) Span:基本工作单元,例如,在一个新建的span中发送一个RPC等同于发送一个回应请求给RPC,span通过 ...

  7. MTK 关闭安全模式

    1.当 Android 设备在安全模式(Safe Mode)下工作时,任何的第三方应用程序或相关文件(主要为apk应用程序文件)都不可以使用,但可以使用 Android 设备的任务管理器选项进行卸载或 ...

  8. Java -- 异常的捕获及处理 -- 目录

    7 异常的捕获及处理 7.1 异常的基本概念 7.1.1 为什么需要异常处理 7.1.2 在程序中使用异常处理 7.1.3 异常类的继承结构 7.1.4 Java的异常处理机制 7.2 throws与 ...

  9. NetBpm 测试篇(3)

    http://www.netbpm.org/movie/holiday/holiday.html

  10. nvm-windows的安装配置

    首先建议把你之前安装的node.js的msi版本给卸载掉. 然后下载nvm-windows并按照默认配置一步步安装 由于国外的镜像源下载慢,所以打开C:\Users\dell\AppData\Roam ...