1.概述
ArrayList 的底层是数组,相当于动态数组(通过System.arrayCopy()方法复制数组),支持随机访问。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
2.继承关系
2.1 Serializable 标记接口
java.io.Serializable 接口,这意味着ArrayList支持序列化,能通过序列化去传输。
序列化:将对象的数据写入到文件(写对象)
反序列化:将文件中对象的数据读取出来(读对象)
import java.io.*;
/**
* @Author blackcat
* @version: 1.0
* @description:序列化和反序列化
* 对象必须实现Serializable 才能写入
*/
public class SerializableTest {
public static void main(String[] args) throws Exception {
//调用写数据的方法
writeObject();
//调用读数据的方法
readObject();
}
//定义方法将对象数据写入到文件
private static void writeObject() throws IOException {
//创建对象操作流 --> 序列化 (将对象的数据写入到文件)
String path = System.getProperty("user.dir");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path+"\\obj.txt"));
//创建学生对象
Student s1 = new Student("张三",32);
//调用对象操作流写对象的方法,将对象的数据写入到文件
oos.writeObject(s1);
//关闭流
oos.close();
}
//定义方法将文件的数据读取出来
private static void readObject() throws IOException, ClassNotFoundException {
//创建对象操作流 --> 反序列化 (将数据从文件中读取出来)
String path = System.getProperty("user.dir");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path+"\\obj.txt"));
//调用方法读取一个对象
Student stu = (Student) ois.readObject();
//关闭流
ois.close();
//输出读取到对象的数据
System.out.println(stu);
}
}
class Student implements Serializable{
//姓名
private String name;
//年龄
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Student{name='");
sb.append(this.name);
sb.append("', age=");
sb.append(this.age);
sb.append("}");
return sb.toString();
}
}
2.2 Cloneable 标记接口
Cloneable ,即覆盖了函数clone(),能被克隆。
import java.util.ArrayList;
/**
* @Author blackcat
* @version: 1.0
* @description:clone 例子
*/
public class ArrayListClone {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("人生就是旅途");
list.add("也许终点和起点会重合");
list.add("但是一开始就站在起点等待终点");
list.add("那么其中就没有美丽的沿途风景和令人难忘的过往");
//调用方法进行克隆
Object o = list.clone();
System.out.println(o == list);
System.out.println(o);
System.out.println(list);
}
}
clone()源码
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
2.3 RandomAccess标记接口
RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。
/*As a rule of thumb, a
* <tt>List</tt> implementation should implement this interface if,
* for typical instances of the class, this loop:
* <pre>
* 随机访问
* for (int i=0, n=list.size(); i < n; i++)
* list.get(i);
* </pre>
* runs faster than this loop:
* <pre>
* 顺序访问
* for (Iterator i=list.iterator(); i.hasNext(); )
* i.next();
* </pre>
*/
//Collections
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
//采用for循环遍历
return Collections.indexedBinarySearch(list, key);
else
//采用迭代器遍历
return Collections.iteratorBinarySearch(list, key);
}
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @Author blackcat
* @version: 1.0
* @description:随机访问与顺序访问的对比
*/
public class RandomAccessTest {
public static void main(String[] args) {
//创建ArrayList集合
List<String> list = new ArrayList<>();
//添加10W条数据
for (int i = 0; i < 100000; i++) {
list.add(i + "a");
}
System.out.println("----通过索引(随机访问:)----");
long startTime = System.currentTimeMillis();
for (int i = 0; i < list.size(); i++) {
//仅仅为了演示取出数据的时间,因此不对取出的数据进行打印
list.get(i);
}
long endTime = System.currentTimeMillis();
System.out.println("随机访问: " + (endTime - startTime));
System.out.println("----通过迭代器(顺序访问:)----");
startTime = System.currentTimeMillis();
Iterator<String> it = list.iterator();
while (it.hasNext()) {
//仅仅为了演示取出数据的时间,因此不对取出的数据进行打印
it.next();
}
endTime = System.currentTimeMillis();
System.out.println("顺序访问: " + (endTime - startTime));
}
}
//----通过索引(随机访问:)----
//随机访问: 4
//----通过迭代器(顺序访问:)----
//顺序访问: 5
2.4 AbstractList抽象类
该类提供实现List接口的骨架实现,以最小实现由随机存储支持接口所需的工作量。对于顺序访问数据应该使用AbstractSequentialList 优先于此类.
3. ArrayList源码分析
3.1 构造方法
| Constructor | Constructor描述 |
|---|---|
| ArrayList() | 无参构造函数 |
| ArrayList(int initialCapacity) | 创建一个初试容量的、空的ArrayList |
| ArrayList(Collection<? extends E> c) | 构造一个包含指定集合的元素的列表 |
public class ArrayList<E> {
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//一个空数组,当用户指定ArrayList容量为0时,返回该数组
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 一个空数组实例
* 当用户没有指定 ArrayList 的容量时(即调用无参构造函数),返回的是该数组==>刚创建一个
* ArrayList 时,其内数据量为 0。
* 当用户第一次添加元素时,该数组将会扩容,变成默认容量为 10(DEFAULT_CAPACITY) 的一个数组
* 它与EMPTY_ELEMENTDATA 的区别就是:该数组是默认返回的,而后者是在用户指定容量为0时返回
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//当前数据对象存放地方,当前对象不参与序列化
transient Object[] elementData;
//ArrayList实际存储的数据数量
private int size;
//继承于AbstractList
//集合数组修改次数的标识
protected transient int modCount = 0;
/**
* 无参构造函数:
* 创建一个空的 ArrayList,此时其内数组缓冲区 elementData = {}, 长度为0
* 当元素第一次被加入时,扩容至默认容量 10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 创建一个初试容量的、空的ArrayList
* @param initialCapacity 初始容量
* @throws IllegalArgumentException 当初试容量值非法(小于0)时抛出
*/
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);
}
}
/**
* 创建一个包含collection的ArrayList
* @param c 要放入 ArrayList 中的集合,其内元素将会全部添加到新建的 ArrayList 实例中
* @throws NullPointerException 当参数 c 为 null 时抛出异常
*/
public ArrayList(Collection<? extends E> c) {
//把集合传化成Object[]数组
elementData = c.toArray();
//转化后的数组长度赋给当前ArrayList的size,并判断是否为0
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
//c.toArray可能不会返回 Object[],可以查看 java 官方编号为 6260652 的 bug
if (elementData.getClass() != Object[].class)
// 若 c.toArray() 返回的数组类型不是 Object[],则利用 Arrays.copyOf();
//来构造一个大小为 size 的 Object[] 数组
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 替换空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
}
Arrays.copyof(char[] original, int newLength)
/*
*复制指定的数组,截断或填充空字符,以便复制具有指定的长度
* original:要复制的数组
*newLength:要返回的数组的长度
*/
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
/*
*将指定源数组的数组,从指定位置,复制到指定数组的指定位置,要赋值的数组元素数量等于length。
* src:源数组
* srcPos:源数组中的起始位置
* dest:目标数组
* destPos:目标数组的起始位置
* length:要赋值的数组元素数量
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
3.2 添加方法
| 方法名 | 描述 |
|---|---|
| public boolean add(E e) | 将指定的元素追加到此列表的末尾。 |
| public void add(int index, E element) | 在此列表中的指定位置插入指定的元素。 |
| public boolean addAll(Collection<?extends E> c) | 按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾。 |
| public boolean addAll(i nt index,Collection<? extends E> c) | 将指定集合中的所有元素插入到此列表中,从指定的位置 |
public class ArrayList<E> {
public boolean add(E e) {
//容量进行校验
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//判断集合存数据的数组是否等于空容量的数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//通过最小容量和默认容量 求出较大值 (用于第一次扩容)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//实际修改集合次数++
modCount++;
//判断最小容量 - 数组长度是否大于 0
// 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);
//判断新容量 - 最小容量 是否小于 0
if (newCapacity - minCapacity < 0)
//还是将最小容量赋值给新容量
newCapacity = minCapacity;
//判断新容量-最大数组大小 是否>0,如果条件满足就计算出一个超大容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 调用数组工具类方法,创建一个新数组,将新数组的地址赋值给elementData
elementData = Arrays.copyOf(elementData, newCapacity);
}
-----------------------------------------------------------
public void add(int index, E element) {
//添加范围检查
rangeCheckForAdd(index);
//调用方法检验是否要扩容,且让增量++
ensureCapacityInternal(size + 1); // Increments modCount
//移动数组
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));
}
---------------------------------------------------------
public boolean addAll(Collection<? extends E> c) {
//把集合的元素转存到Object类型的数组中
Object[] a = c.toArray();
//记录数组的长度
int numNew = a.length;
//调用方法检验是否要扩容,且让增量++
ensureCapacityInternal(size + numNew); // Increments modCount
//调用方法将a数组的元素拷贝到elementData数组中
System.arraycopy(a, 0, elementData, size, numNew);
//集合的长度+=a数组的长度
size += numNew;
//只要a数组的长度不等于0,即说明添加成功
return numNew != 0;
}
---------------------------------------------------------
public boolean addAll(int index, Collection<? extends E> c) {
//校验索引
rangeCheckForAdd(index);
//将数据源转成数组
Object[] a = c.toArray();
//记录数组的长度
int numNew = a.length;
//调用方法检验是否要扩容,且让增量++
ensureCapacityInternal(size + numNew); // Increments modCount
//numMoved:代表要移动元素的个数
int numMoved = size - index;
if (numMoved > 0)
// 第一次数组复制,从elementData中的index位置开始,复制到index + numNew位置上, //复制numMoved个元素
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
// 第二次数组复制,从a 数组中的第0个位置开始,复制到elementData第index位置上你,复制 //numNew个元素
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
//只要a数组的长度不等于0,即说明添加成功
return numNew != 0;
}
}
3.3 删除方法
public class ArrayList<E> {
public E remove(int index) {
//范围校验
rangeCheck(index);
//增量+1
modCount++;
//将index对应的元素赋值给 oldValue
E oldValue = elementData(index);
//计算集合需要移动元素个数
int numMoved = size - index - 1;
if (numMoved > 0)
//如果需要移动元素个数大于0,就使用arrayCopy方法进行拷贝
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) {
//判断要删除的元素是否为null
if (o == null) {
//遍历集合
for (int index = 0; index < size; index++)
//判断集合的元素是否为null
if (elementData[index] == null) {
//如果相等,调用fastRemove
fastRemove(index);
return true;
}
} else {
//遍历集合
for (int index = 0; index < size; index++)
//如果相等,调用fastRemove
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)
//如果需要移动元素个数大于0,就使用arrayCopy方法进行拷贝
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将源集合最后一个元素置为null,尽早让垃圾回收机制对其进行回收
elementData[--size] = null; // clear to let GC do its work
}
}
3.4 修改方法
public class ArrayList<E> {
public E set(int index, E element) {
//范围校验
rangeCheck(index);
//取出index对应的元素
E oldValue = elementData(index);
//将element直接覆盖index对应的元素
elementData[index] = element;
//返回被覆盖的元素
return oldValue;
}
}
3.5 获取方法
public class ArrayList<E> {
public E get(int index) {
//范围校验
rangeCheck(index);
return elementData(index);
}
}
3.6 迭代器方法
public class ArrayList<E> {
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
// 下一个要返回元素的索引
int cursor; // index of next element to return
// 最后一个返回元素的索引
int lastRet = -1; // index of last element returned; -1 if no such
//将实际修改集合次数赋值给预期修改次数
//在迭代的过程中,只要实际修改次数和预期修改次数不一致就会产生并发修改异常
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
//把下一个元素的索引赋值给i
int i = cursor;
//判断是否有元素
if (i >= size)
throw new NoSuchElementException();
//将集合底层存储数据的数组赋值给迭代器的局部变量 elementData
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
//每次成功获取到元素,下一个元素的索引都是当前索引+1
cursor = i + 1;
//返回元素
return (E) elementData[lastRet = i];
}
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();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
//如果预期修改次数 和 实际修改次数不相等 就产生并发修改异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
3.7 清空方法
public class ArrayList<E> {
public void clear() {
//实际修改集合次数++
modCount++;
//遍历集合,将集合每一个索引对应位置上的元素都置为null,尽早让其释放
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
//集合长度为0
size = 0;
}
}
3.8判断集合是否为空
public class ArrayList<E> {
public boolean isEmpty() {
return size == 0;
}
}
3.9 包含方法
public class ArrayList<E> {
public boolean contains(Object o) {
//调用indexOf方法进行查找
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
//如果元素是null,也进行遍历操作
//因为集合中有可能够会存储null
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;
}
//不存在返回-1
return -1;
}
}
4.自定义ArrayList
public class MyArrayList<E> {
//定义数组,用于存储集合的元素
private Object[] elementData;
//定义变量,用于记录数组的个数
private int size;
//定义空数组,用于在创建集合对象的时候给elementData初始化
private Object[] emptyArray = {};
//定义常量,用于记录集合的容量
private final int DEFAULT_CAPACITY = 10;
//构造方法
public MyArrayList(){
//给elementData初始化
elementData = emptyArray;
}
//定义add方法
public boolean add(E e){
//将来再调用的时候需要判断是否需要扩容
grow();
//将元素添加到集合
elementData[size++] = e;
return true;
}
//简单扩容
private void grow(){
//判断集合存储元素的数组是否等于emptyArray
if(elementData == emptyArray){
//第一次扩容
elementData = new Object[DEFAULT_CAPACITY];
}
//核心算法 1.5倍
//如果size==集合存元素数组的长度,就需要扩容
if(size == elementData.length){
//先定义变量记录老容量
int oldCapacity = elementData.length;
//核心算法 1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//创建一个新的数组,长度就newCapacity
Object[] obj = new Object[newCapacity];
//拷贝元素
System.arraycopy(elementData, 0, obj, 0, elementData.length);
//把新数组的地址赋值给elementData
elementData = obj;
}
}
//转换方法
public String toString(){
//建议对集合进行判断,如果没有内容直接返回 "[]"
if(size == 0){
return "[]";
}
//创建StringBuilder
StringBuilder sb = new StringBuilder();
sb.append("[");
//循环遍历数组
for (int i = 0; i < size; i++) {
//判断i是否等于size-1
if(i == size-1){
//要追加元素,还需要追加]
sb.append(elementData[i]).append("]");
}else{
sb.append(elementData[i]).append(",").append(" ");
}
}
//把sb中的所有数据转成一个字符串,且返回
return sb.toString();
}
//修改方法
public E set(int index, E element) {
//建议先对方法的参数索引进行判断
checkIndex(index);
//把index索引对应的元素取出来
E value = (E) elementData[index];
//替换元素
elementData[index] = element;
return value;
}
private void checkIndex(int index) {
if(index < 0 || index >= size){
//制造一个异常
throw new IndexOutOfBoundsException("索引越界了!");
}
}
//删除方法
public E remove(int index) {
//校验索引
checkIndex(index);
//取出元素
E value = (E) elementData[index];
//计算出要移动元素的个数
int numMoved = size - index - 1;
//判断要移动的个数是否大于0
if (numMoved > 0){
System.arraycopy(elementData, index+1, elementData, index, numMoved);
}
//把最后一个位置上的元素置为null
elementData[--size] = null;
return value;
}
//根据索引获取元素
public E get(int index){
//调用方法校验索引
checkIndex(index);
//直接从elementData数组中获取元素且返回
return (E) elementData[index];
}
//获取集合的长度
public int getSize(){
return size;
}
}