jdk1.8 API
黑马教学视频: java进阶教程丨全面深入解析ArrayList原理(源码分析+面试讲解)

执行这段代码 ArrayList 看一下无参构造干了些什么事
//真正存储数据的容器
transient Object[] elementData;
//默认空容量的数组,长度为 0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 空参构造
public ArrayList() {
//初始化赋值一个空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
执行这段代码 ArrayList 看一下有一个整型参构造干了些什么事
//真正存储数据的容器
transient Object[] elementData;
//空容量的数组,长度为 0
private static final Object[] EMPTY_ELEMENTDATA = {};
// 有参构造
public ArrayList(int initialCapacity) {
//接入参值 ,初始化数据
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { //接入参值为0 ,初始化数据
this.elementData = EMPTY_ELEMENTDATA;
} else { //其它就抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
执行下面这段代码
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("aaa");
arrayList.add("bbb");
arrayList.add("ccc");
ArrayList<String> list = new ArrayList<>(arrayList);
for (String s:list ) {
System.out.println(s);
}
有参构造: [指定合集参数]
//真正存储数据的容器
transient Object[] elementData;
//长度为 0的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 集合长度
private int size;
// 有参构造
public ArrayList(Collection<? extends E> c) {
// 将构造方法中的参数转为数组
elementData = c.toArray();
//将新数组长度赋值给size
if ((size = elementData.length) != 0) {
// 再次进行判断
if (elementData.getClass() != Object[].class)
//数组的创建和拷贝
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 就把空数组的地址赋值给集合存元素的数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
//将集合转为数组的请求
public Object[] toArray() {
//调用工具类的方法 elementData 源数据, size 是源数组长度
return Arrays.copyOf(elementData, size);
}
//工具类
class Arrays {
//
public static <T> T[] copyOf(T[] original, int newLength) {
//再次调用方法得到一个数组
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
//不管三元运算符结果如何,都会创建一个新的数组;
//新的数组的长度一定是和集合的size 一样
T[] copy = ((Object)newType == (Object)Object[].class) //此处比较地址-->详解看第7节Arrays.copyOf()
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
//新数组的拷贝 :把源数组拷贝到新创建的数组中
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
//返回新数组
return copy;
}
}
Api 文档如下

1、指定的元素追加到此列表末尾 源码分析
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<>();
strList.add("1");
}
//真正存储数据的容器
transient Object[] elementData;
//默认空容量的数组,长度为 0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//长度为 0的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认 初始化数组容量为 10
private static final int DEFAULT_CAPACITY = 10;
//2 The size of the ArrayList (the number of elements it contains). [ArrayList的大小(它包含的元素数)]
private int size;
//初始化数组的修改次数
protected transient int modCount = 0;
//1 Appends the specified element to the end of this list. [在list 末尾追加一个指定元素]
public boolean add(E e) {
// 确保内部容量,size是添加前数组内元素的数量
ensureCapacityInternal(size + 1); // size 默认0
//添加数据
elementData[size++] = e;
return true;
}
// 3 确保内部容量可用,size是添加前数组内元素的数量
private void ensureCapacityInternal(int minCapacity) {
//elementData 初始化的存储容器
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 4 计算数组长度
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//判断 集合存元素的地址是否为默认空容量数组元素
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //当执行new ArrayList() 时会初始化引用
//返回最大的值
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//明确的数组容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// minCapacity 当前真实数组容量大小 - 初始化数组元素容量大小({}.length = 10) > 0
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
/**
*
* @DESC 核心扩容方法
* @param minCapacity 当前真实数组容量大小
* 扰动
**/
private void grow(int minCapacity) {
// 初始的时候elementData= {} 所以旧的容量 oldCapacity= {}.length = 0
int oldCapacity = elementData.length;
// 右移:>> 右移几位就相当于除以2的n 次幂
// 左移:<< 左移几位就相当于乘以2的n 次幂
// 新容量 = 旧容量+右移过后的值
int newCapacity = oldCapacity + (oldCapacity >> 1); //源容量的1.5倍
// 赋值新容量大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新容量大小 跟 integer 最大容量比较
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);
}
//超大容量方法
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2、在此列表中的指定位置插入指定的元素 源码分析
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<>();
strList.add("2");
strList.add("3");
strList.add(1,"1");
}
//真正存储数据的容器
transient Object[] elementData;
//默认空容量的数组,长度为 0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//长度为 0的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认 初始化数组容量为 10
private static final int DEFAULT_CAPACITY = 10;
//2 The size of the ArrayList (the number of elements it contains). [ArrayList的大小(它包含的元素数)]
private int size;
//初始化数组的修改次数
protected transient int modCount = 0;
//
public void add(int index, E element) {
//
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将数组元素elementData 从索引位置1 开始
// 还是拷贝到elementData 索引位置为index +1 位置
// 数组元素为 size-index 之后的数组元素
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 数组指定位置赋值
elementData[index] = element;
size++;
}
//检查 索引范围
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 3 确保内部容量可用,size是添加前数组内元素的数量
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 4 计算数组长度
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//5 确保数组容量,然后进行扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// minCapacity 当前真实数组容量大小 - 数组元素容量大小(elementData.length = 10) < 0
if (minCapacity - elementData.length > 0) // 因为已经添加过元素,最后一次是指定索引添加元素,初始值已经有值
// 只有容量不够才去扩容
grow(minCapacity);
}
指定位置赋值内存地址分析,如下图解



3、按指定集合的Iterator 返回顺序将指定集合中的所有元素追加到此列表末尾 源码分析
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<>();
strList.add("2");
strList.add("3");
strList.add(1,"1");
}
//真正存储数据的容器
transient Object[] elementData;
//默认空容量的数组,长度为 0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//长度为 0的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认 初始化数组容量为 10
private static final int DEFAULT_CAPACITY = 10;
//2 The size of the ArrayList (the number of elements it contains). [ArrayList的大小(它包含的元素数)]
private int size;
//初始化数组的修改次数
protected transient int modCount = 0;
public boolean addAll(Collection<? extends E> c) {
//将有数据的集合转换为数组
Object[] a = c.toArray();
//将有数据的集合的长度赋值给numNew
int numNew = a.length;
//校验 计算数组容量,首次扩容
ensureCapacityInternal(size + numNew); // Increments modCount
//真正的拷贝:把a元素整体拷呗到elementData中
System.arraycopy(a, 0, elementData, size, numNew);
//集合长度进行更改:size 的大小为数据元素a的长度
size += numNew;
return numNew != 0;
}
// 3 确保内部容量可用
private void ensureCapacityInternal(int minCapacity) {
//elementData 初始化的存储容器
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 4 计算数组长度
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++;
// minCapacity 当前真实数组容量大小 - 初始化数组元素容量大小({}.length = 10) > 0
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
/**
*
* @DESC 核心扩容方法
* @param minCapacity 当前真实数组容量大小
* 扰动
**/
private void grow(int minCapacity) {
// 初始的时候elementData= {} 所以旧的容量 oldCapacity= {}.length = 0
int oldCapacity = elementData.length;
// 右移:>> 右移几位就相当于除以2的n 次幂
// 左移:<< 左移几位就相当于乘以2的n 次幂
// 新容量 = 旧容量+右移过后的值
int newCapacity = oldCapacity + (oldCapacity >> 1); //源容量的1.5倍
// 赋值新容量大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新容量大小 跟 integer 最大容量比较
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);
}
//超大容量方法
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
结论:底层使用了System.arraycopy 的方法进行拷贝;
4、 将指定集合的所元素插入到此列表中,从指定的位置开始 源码分析
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("黑马程序员");
list.add("传智播客");
list.add("传智大学");
ArrayList<String> listSource = new ArrayList<String>();
listSource.add("醋丁鱼");
listSource.add("博学谷");
listSource.addAll(1,list);
}
//真正存储数据的容器
transient Object[] elementData;
//默认空容量的数组,长度为 0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//长度为 0的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认 初始化数组容量为 10
private static final int DEFAULT_CAPACITY = 10;
//2 The size of the ArrayList (the number of elements it contains). [ArrayList的大小(它包含的元素数)]
private int size;
//初始化数组的修改次数
protected transient int modCount = 0;
public boolean addAll(int index, Collection<? extends E> c) {
//检验索引 (index > size || index < 0)
rangeCheckForAdd(index);
//将有数据的集合转换为数组
Object[] a = c.toArray();
//记录数据源长度
int numNew = a.length;
// 目的就是为了给集合存储数据的数组进行扩容
ensureCapacityInternal(size + numNew); // Increments modCount
//numMoved 要移动的元素的个数 = 数据目的长度-addAll的第一个参数(索引)
//size: 数据目的长度(也就是最终添加到的那个元素的长度)
//现在需要添加元素,需要后面的元素向后移动留出位置,先计算移动的元素的个数,使用公式size - index即可
int numMoved = size - index;
if (numMoved > 0) //如果移动个数大于0
// 数据目的集合首先自己先做对应拷贝
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 真正的将数据源中的所有数据添加到数据目的
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
//检查 索引范围
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 3 确保内部容量可用
private void ensureCapacityInternal(int minCapacity) {
//elementData 初始化的存储容器
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 4 计算数组长度
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++;
// minCapacity 当前真实数组容量大小 - 初始化数组元素容量大小({}.length = 10) > 0
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
/**
*
* @DESC 核心扩容方法
* @param minCapacity 当前真实数组容量大小
* 扰动
**/
private void grow(int minCapacity) {
// 初始的时候elementData= {} 所以旧的容量 oldCapacity= {}.length = 0
int oldCapacity = elementData.length;
// 右移:>> 右移几位就相当于除以2的n 次幂
// 左移:<< 左移几位就相当于乘以2的n 次幂
// 新容量 = 旧容量+右移过后的值
int newCapacity = oldCapacity + (oldCapacity >> 1); //源容量的1.5倍
// 赋值新容量大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新容量大小 跟 integer 最大容量比较
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);
}
//超大容量方法
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
指定位置插入集合内存地址分析,如下图解


3、移动自身元素之后,拷贝源数组元素到目的数组元素中

指定位置插入集合移动位置分析,代码模拟添加位置,代码如下
public static void main(String[] args) {
String[] array1 = {"黑马程序员","传智播客","传智大学"};
//添加空值模拟初始化集合后的10个空间长度
String[] array2 = {"醋丁鱼","博学谷",null,null,null,null,null,null,null,null};
int numNew = array1.length;
//size - index size:目标数组长度 - 索引
int numMoved = 2 - 1;
if (numMoved > 0)
System.arraycopy(array2, 1, array2, 1 + numNew,
numMoved);
// 复制 array2 元素 从索引位置1开始 目标位置也是array2 开始存储数据的索引位置1+numNew 源数组的数量:要移动的个数numMoved
System.out.println("第一次自身拷贝:"+Arrays.toString(array2)); // 1+ numNew 就是要留出array1元素的位置
// {"醋丁鱼","博学谷",null,null,博学谷,null,null,null,null,null};
System.arraycopy(array1, 0, array2, 1, numNew);
System.out.println("第二次源数组给目标数据拷贝:"+Arrays.toString(array2));
}
第一次自身拷贝:[醋丁鱼, 博学谷, null, null, 博学谷, null, null, null, null, null]
第二次源数组给目标数据拷贝:[醋丁鱼, 黑马程序员, 传智播客, 传智大学, 博学谷, null, null, null, null, null]
Api jdk1.8
1、remove(int index) 删除该列表中指定位置的元素。
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("黑马程序员");
list.add("传智播客");
list.add("传智大学");
list.remove(1);
}
public E remove(int index) {
// 检查索引是否超出范围,如果超出抛出异常:IndexOutOfBoundsException
rangeCheck(index);
// 标记修改数量统计
modCount++;
//取出索引位置处元素,赋值给oldValue
E oldValue = elementData(index);
//numMoved 要移动的元素的个数 = 数据目的长度-addAll的第一个参数(索引)
//size: 数据目的长度(也就是最终添加到的那个元素的长度)
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
return oldValue;
}
E elementData(int index) {
return (E) elementData[index];
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
public static void main(String[] args) {
String[] array1 = {"黑马程序员","传智播客","传智大学"};
String[] removeIndexElementMoc = removeIndexElementMoc(array1, 1);
System.out.println(Arrays.toString(removeIndexElementMoc));
}
/**
*
* @param index 要删除数据的索引位置
*/
private static String[] removeIndexElementMoc(String[] array ,int index) {
int size = array.length;
String oldValue = array[index];
//要移动的个数numMoved
int numMoved = size - index - 1;
if (numMoved > 0)
// 复制 array 元素 从索引位置index+1开始 array 开始存储数据的索引位置 index 源数组的数量
System.arraycopy(array, index+1, array, index, numMoved);
//分析 index + 1 : 数组位置元素为: 传智大学
//分析 numMoved : 要复制的源数组数量
array[--size] = null; // clear to let GC do its work
return array;
}
[黑马程序员, 传智大学, null]
2、remove(Object o) 从列表中删除指定元素的第一个出现(如果存在)。
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("黑马程序员");
list.add("传智播客");
list.add("传智大学");
list.remove("传智大学");
}
public boolean remove(Object o) {
// 如果为null ,则删除空元素,否则删除指定元素
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++;
//要移动的个数numMoved
int numMoved = size - index - 1;
if (numMoved > 0)
//拷贝元素elementData 从索引位置 index+1 到目标元素elementData 从第index 位置开始覆盖到 numMoved 结束
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
3、removeAll(Collection> c) 从此列表中删除指定集合中包含的所有元素。
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("黑马程序员");
list.add("传智播客");
list.add("传智大学");
ArrayList<String> list2 = new ArrayList<>();
list.add("传智大学");
list.removeAll(list2);
}
public boolean removeAll(Collection<?> c) {
// 校验集合是否为空
Objects.requireNonNull(c);
return batchRemove(c, false);
}
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
/**
* @Param c : 被删除的集合列表
* @Param complement:
* 批量删除
**/
private boolean batchRemove(Collection<?> c, boolean complement) {
// 把源数组赋值给 局部elementData 数组
final Object[] elementData = this.elementData;
//r用来控制循环,w是记录有多少个差集 removeAll 调用记录差集
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
// 校验c集合元素是否在elementData中:是否为false/true
if (c.contains(elementData[r]) == complement)
// 若是 complement = true 则将 c中包含elementData中的元素 替换 elementData[w++]位置值 达到元素前移效果
// 若是 complement = false则将 c中不包含elementData中的元素 存入 elementData[w++]位置值 达到元素前移效果
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) { //将 自 elementData[w] 到 elementData[size-1]的值全部置为null
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w; // 标记elementData数组修改次数
size = w;
modified = true;
}
}
return modified;
}
// 集合是否包含元素O
public boolean contains(Object o) {
return indexOf(o) >= 0; //没有数据则为-1
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
4、removeIf(Predicate super E> filter) removeIf()的入参是一个过滤条件,用来判断需要移除的元素是否满足条件。
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
arr.removeIf(item->"d".equals(item));
System.out.println(arr);
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
// 用于记录删除数量的局部变量
int removeCount = 0;
//removeSet 用来记录被 删除的元素的下标,节省空间且有去重性
final BitSet removeSet = new BitSet(size);
//expectedModCount 线程安全的统计量
final int expectedModCount = modCount;
final int size = this.size;
//把符合条件的待删除元素的下标设置到BitSet中,即BitSet中对应的下标设为true
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
// 检测是否有其它线程并发修改这个ArrayList
// 这个就是并发是fail-fast机制,可以让当前线程快速失败,而不会产生资源竞争,导致锁之类的现象
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
//
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
1、set(int index, E element) 用指定的元素替换此列表中指定位置的元素。
ArrayList<String> list = new ArrayList<>();
list.add("黑马程序员");
list.add("传智播客");
list.add("传智大学");
System.out.println(list);
Object set = list.set(0, "安锐捷大学");
System.out.println(set);
System.out.println(list);
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
输出结果如下
[黑马程序员, 传智播客, 传智大学]
黑马程序员
[安锐捷大学, 传智播客, 传智大学]
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
1、toString() 返回此集合的字符串表示形式。
ArrayList<String> list = new ArrayList<>();
list.add("黑马程序员");
list.add("传智播客");
list.add("传智大学");
String s = list.toString();
System.out.println(list);
public abstract class AbstractCollection<E> implements Collection<E> {
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
//无限循环
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
}
输出结果如下
[黑马程序员, 传智播客, 传智大学]
1、public Iterator iterator() 普通 迭代器。
案例1 已知集合: List list = new ArrayList();里面有三个元素 hello,java,php 使用迭代器遍历获取集合的每一个元素
首先需要明确的是,toString() 方法不是ArrayList 的方法,而是继承的AbstractCollection 的方法
public static void main(String[] args) {
List<String> arrayList = new ArrayList<String>();
arrayList.add("hello");
arrayList.add("java");
arrayList.add("php");
Iterator<String> it = arrayList.iterator();
while (it.hasNext()){
String next = it.next();
System.out.println(next);
}
}
// 获取迭代器的方法
public Iterator<E> iterator() {
return new Itr(); // 创建了一个对象
}
/**
* AbstractList.Itr 内部类 迭代器源码
*/
private class Itr implements Iterator<E> {
int cursor; //光标索引 下一个光标位置索引 index of next element to return
int lastRet = -1; //记录-1 index of last element returned; -1 if no such
int expectedModCount = modCount; // 将集合实际修改的次数赋值给预期修改的次数
Itr() {}
//判断集合是否有元素
public boolean hasNext() {
// 光标是否不等于集合的size
return cursor != size;
}
//返回下一个元素
public E next() {
//校验:
checkForComodification();
// 初始化时 i = 0
int i = cursor;
// 判断,如果大于集合的size 就说明没有元素了
if (i >= size)
throw new NoSuchElementException();
// 把集合存储数据数组的地址赋值给此方法的局部变量
Object[] elementData = ArrayList.this.elementData;
//进行判断 ,如果条件满足就会产生并发修改异常
if (i >= elementData.length)
throw new ConcurrentModificationException();
//光标自增
cursor = i + 1;
//从数组中取出元素且返回
return (E) elementData[lastRet = i];
}
// 校验预期修改集合次数是否和实际修改集合次数一样
final void checkForComodification() {
if (modCount != expectedModCount)
// 如果不一样就会产生并发修改异常
throw new ConcurrentModificationException();
}
}
输出结果如下
hello
java
php
案例2 已知集合: List list = new ArrayList();里面有三个元素 hello,java,php 使用迭代器遍历获取集合看是否有"php" 这个元素,如果有,就使用集合对象删除该元素
public static void main(String[] args) {
List<String> arrayList = new ArrayList<String>();
arrayList.add("hello");
arrayList.add("java");
arrayList.add("php");
Iterator<String> it = arrayList.iterator();
while (it.hasNext()){
String next = it.next();
if(next.equals("php")){
arrayList.remove("php");
}
}
}
//----------------------------添加数据源码 操作----------------------------
// 添加数据源码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 确保内部容量可用
private void ensureCapacityInternal(int minCapacity) {
//elementData 初始化的存储容器
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 计算数组长度
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++;
// minCapacity 当前真实数组容量大小 - 初始化数组元素容量大小({}.length = 10) > 0
if (minCapacity - elementData.length > 0)
//扩容
grow(minCapacity);
}
//----------------------------迭代器源码 操作----------------------------
// 获取迭代器的方法
public Iterator<E> iterator() {
return new Itr(); // 创建了一个对象,初始化成员变量
}
/**
* AbstractList.Itr 内部类 迭代器源码
*/
private class Itr implements Iterator<E> {
int cursor; //光标索引 下一个光标位置索引 index of next element to return
int lastRet = -1; //记录-1 index of last element returned; -1 if no such
// 获取迭代器的时候,expectedModCount 的值(也就是集合修改的次数)
int expectedModCount = modCount; // 将集合实际修改的次数赋值给预期修改的次数
Itr() {}
//判断集合是否有元素
public boolean hasNext() {
// 光标是否不等于集合的size
return cursor != size;
}
//返回下一个元素
public E next() {
//校验:
checkForComodification();
// 初始化时 i = 0
int i = cursor;
// 判断,如果大于集合的size 就说明没有元素了
if (i >= size)
throw new NoSuchElementException();
// 把集合存储数据数组的地址赋值给此方法的局部变量
Object[] elementData = ArrayList.this.elementData;
//进行判断 ,如果条件满足就会产生并发修改异常
if (i >= elementData.length)
throw new ConcurrentModificationException();
//光标自增
cursor = i + 1;
//从数组中取出元素且返回
return (E) elementData[lastRet = i];
}
// 校验预期修改集合次数是否和实际修改集合次数一样
final void checkForComodification() {
if (modCount != expectedModCount)
//如果不一样,就会产生并发修改异常
throw new ConcurrentModificationException();
}
}
//集合删除元素的方法
public boolean remove(Object o) {
// 判断要删除的元素是否为null ,
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) {
// 在删除的方法中集合实际修改次数会自增
// 集合实际修改次数为:4 但是预期修改次数为 3
modCount++;
//计算集合要移动元素的个数
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
}
(modCount++;)(int expectedModCount = modCount) fastRemove()方法的modCount++;Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at source.Iterator_Analyse.main(Iterator_Analyse.java:22)
案例3 已知集合: List list = new ArrayList();里面有三个元素 hello,php,java 使用迭代器遍历获取集合看是否有"php" 这个元素,如果有,就使用集合对象删除该元素
public static void main(String[] args) {
List<String> arrayList = new ArrayList<String>();
arrayList.add("hello");
arrayList.add("php");
arrayList.add("java");
Iterator<String> it = arrayList.iterator();
while (it.hasNext()){
String next = it.next();
if(next.equals("php")){
arrayList.remove("php");
}
}
}
结论:
原因:
迭代器默认方法源码分析
List<String> arrayList = new ArrayList<String>();
arrayList.add("hello");
arrayList.add("php");
arrayList.add("java");
Iterator<String> it = arrayList.iterator();
while (it.hasNext()){
String next = it.next();
if(next.equals("php")){
// 迭代器删除
it.remove();
}
}
//------------------迭代器自带删除方法--------------------------
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//内部类调用外部类删除方法
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//把实际修改集合次数赋值给预期修改次数
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
//-----------------集合删除的方法 ----------------------------
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);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
结论:
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
// 集合是否包含元素O
public boolean contains(Object o) {
return indexOf(o) >= 0; //没有数据则为-1
}
public int indexOf(Object o) {
if (o == null) { //判断 元素是否为null
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {// 取集合中的每一个元素和要找的元素进行比较内容
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
boolean isEmpty() 如果此列表不包含元素,则返回 true 。
源码
public boolean isEmpty() {
return size == 0;
}