只记录目前为止关注的。JDK1.8

一、基础属性

1.1 内部参数

    //空存储实例。直接new ArrayList()便是以该空数组作为实例
private static final Object[] EMPTY_ELEMENTDATA = {}; //默认容量大小,在由空实例进行首次扩容时,扩到到该长度。
//实际使用中,并未实际存在Capacity这个参数,需要扩容时直接根据旧数组的length进行扩容
private static final int DEFAULT_CAPACITY = 10; //实际存储数组
transient Object[] elementData; //存储元素个数
private int size; //该字段用于迭代器的fail-fast机制【参考另一篇文章】
protected transient int modCount = 0;

1.2 三个重载构造方法

    //构建一个空实例,elementData指向容量为0的空数组
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);
}
} //以集合初始化创建列表
//步骤:1. 调用toArray(),将给定集合转成数组(以ArrayList为例,toArray()返回的是实际存在元素的那部分数组,即[0,size))
//2. 让size直接等于给定集合的数组长度
//3. 判断size如果为0则直接创建空存储实例,否则使用Arrays.copyOf将给定集合数组复制一份(浅拷贝),作为存储数组
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;
}
} // ArrayList 的 toArray()源码,复制[0,size)部分返回
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}

二、操作及策略

2.1 动态扩容

扩容策略:当数组全满了才扩容,新长度=旧长度*1.5

动态扩容有两个入口:供用户调用的显式扩容ensureCapacity()和添加元素时的隐式扩容ensureCapacityInternal(),不过均是调用ensureExplicitCapacity()来根据传入所需容量值决定是否扩容,最终实际扩容操作在grow()方法中。

    //显式扩容入口方法
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY; if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
} //隐式扩容入口方法
//其中参数 minCapacity 值为 size+1,由add方法调用传入
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
} //添加方法
public boolean add(E e) {
// 传入最小所需容量为size+1
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
} //计算所需容量,实则不需要计算
//只是单纯判断当前是否是空实例,为空就话返回 "默认容量"与minCapacity之间的较大值,不为空直接返回minCapacity
//参数 minCapacity 只有两种情况:
// 1. 隐式扩容时,如add()传入,其值为 size+1
// 2. 显式扩容,用户指定
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
} //在此处,根据最小所需容量来判断是否实际进行扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0) //若已经占满,才进行扩容
grow(minCapacity);
} //实际扩容方法
//可以看到扩容策略为:length + length*0.5(右移1位缩小1倍)
//然后调用Arrays.copyOf浅复制
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //旧长度+旧长度*2
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);
}

2.2 删除操作

ArrayList中有两个remove方法public E remove(int index)public boolean remove(Object o)

源码实现得其实挺简单,本来还在犹豫有没有必要把remove方法记录下来,但发现有个点值得注意以下:

注意点:JDK对于数组内元素统一移动操作,偏好使用System.arraycopy(ary, srcPos, ary, destPos, len)来移动


//通过给定下标来删除元素,并返回删除元素
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); // 使用System.arraycopyl来移动数组元素
elementData[--size] = null; // clear to let GC do its work // 删除后size-1,并将先前最后一个元素置null return oldValue; //返回删除元素
} // 判断下标是否合理(<size),否则抛出异常
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
} // 删除首个与给定元素相等的元素,元素判等使用equal方法
// 成功返回true,失败返回false
public boolean remove(Object o) {
if (o == null) { // 可以删除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])) { // 从头开始一个个检查,使用equal判等
fastRemove(index);
return true;
}
}
return false;
} /*
* 官方注释说得很清楚了:删除元素,但跳过边界检查且不返回删除元素。(就是remove(index)的缩略版
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
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
}

Java ArrayList 详解的更多相关文章

  1. Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理

    本文非常详尽地介绍了Java中的三个集合类 ArrayList,Vector与Stack <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整 ...

  2. Java synchronized 详解

    Java synchronized 详解 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 1.当两个并发线程访问同一个对象object ...

  3. java集合详解

    1.java集合框架的层次结构 Collection接口: Set接口: HashSet具体类 LinkedHashSet具体类 TreeSet具体类 List接口:   ArrayList具体类 L ...

  4. 【集合框架】JDK1.8源码分析之ArrayList详解(一)

    [集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...

  5. java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...

  6. Java List详解,面试中应该如何解答关于List的问题

    对于面试,我们在介绍Java的List的时候,一般需要介绍到,什么是List?List包括什么?各自在用法上有什么区别,在存储上有什么区别?List需要注意什么?把这些问题串起来,我们可以这样介绍: ...

  7. Java集合详解8:Java的集合类细节精讲

    Java集合详解8:Java集合类细节精讲 今天我们来探索一下Java集合类中的一些技术细节.主要是对一些比较容易被遗漏和误解的知识点做一些讲解和补充.可能不全面,还请谅解. 本文参考:http:// ...

  8. Java集合详解3:Iterator,fail-fast机制与比较器

    Java集合详解3:Iterator,fail-fast机制与比较器 今天我们来探索一下LIterator,fail-fast机制与比较器的源码. 具体代码在我的GitHub中可以找到 https:/ ...

  9. Java泛型详解(转)

    文章转自  importNew:Java 泛型详解 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用.本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理 ...

随机推荐

  1. OpenCV中的图像形态学转换

    两个基本的形态学操作是腐蚀和膨胀.他们的变化构成了开运算,闭运算,梯度等.下面以这张图为例 1.腐蚀 这个操作会把前景物体的边界腐蚀掉. import cv2 import numpy as np i ...

  2. (转)可简化iOS 应用程序开发的6个Xcode小技巧

    Xcode是iPhone和iPad开发者用来编码或者开发iOS app的IDE.Xcode有很多小巧但很有用的功能,很多时候我们可能没有注意到它们,也或者我们没有在合适的水平使用这些功能简化我们的iO ...

  3. Nastya Studies Informatics CodeForces - 992B (大整数)

    B. Nastya Studies Informatics time limit per test 1 second memory limit per test 256 megabytes input ...

  4. 图上两点之间的第k最短路径的长度 ACM-ICPC 2018 沈阳赛区网络预赛 D. Made In Heaven

    131072K   One day in the jail, F·F invites Jolyne Kujo (JOJO in brief) to play tennis with her. Howe ...

  5. HDU 4729 An Easy Problem for Elfness 主席树

    题意: 给出一棵树,每条边有一个容量. 有若干次询问:\(S \, T \, K \, A \, B\),求路径\(S \to T\)的最大流量. 有两种方法可以增大流量: 花费\(A\)可以新修一条 ...

  6. 洛谷P1085不高兴的津津

  7. shutil——高级的 文件、文件夹、压缩包 处理模块

    高级的 文件.文件夹.压缩包 处理模块 shutil.copyfileobj(fsrc, fdst[, length])复制文件内容(不包含元数据)从类文件对象src到类文件对dst.可选参数leng ...

  8. sql server 学习分享

    http://www.cnblogs.com/liu-chao-feng/p/6144872.html

  9. [错误处理]UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)

    Stackoverflow 回答: 将byte类型转化:byte_string.decode('utf-8') Jinja2 is using Unicode internally which mea ...

  10. set(集合)类

    1.set(集合)和 map(映射) 都属于关联容器,它们都支持查询一个元素是否存在,并能有效地获取元素.实现了红黑树的平衡二叉检索树的数据结构,插入元素时,它会自动调整二叉树的排列,把元素放到适当的 ...