ArrayList源码详解篇

if (obj == null)

throw new NullPointerException();

return obj;

}

@Override

public Object[] toArray() {

// 返回 a 的克隆对象

return a.clone();

}

}

这是Arrays.asList()方法源码

public static List asList(T… a) {

return new ArrayList<>(a);

}

不难看出来java.util.Arrays的内部ArrayList的toArray()方法,是构造方法接收什么类型的数组,就返回什么类型的数组。

所以,在我们上面的例子中,实际上返回的是String类型的数组,再将其中的元素赋值成Object类型的,自然报错。

🎠插入方法


在列表最后添加指定元素

/**

  • 在列表最后添加指定元素

  • @param e 要添加的指定元素

  • @return true

*/

public boolean add(E e) {

// 增加 modCount !!

ensureCapacityInternal(size + 1);

elementData[size++] = e;

return true;

}

在父类AbstractList上,定义了modCount 属性,用于记录数组修改的次数。

在指定位置添加指定元素

/**

  • 在指定位置添加指定元素

  • 如果指定位置已经有元素,就将该元素和随后的元素移动到右面一位

  • @param index 待插入元素的下标

  • @param element 待插入的元素

  • @throws 可能抛出 IndexOutOfBoundsException

*/

public void add(int index, E element) {

rangeCheckForAdd(index);

// 增加 modCount !!

ensureCapacityInternal(size + 1);

System.arraycopy(elementData, index, elementData, index + 1,

size - index);

elementData[index] = element;

size++;

}

插入方法调用的其他私有方法

/**

  • 计算容量

*/

private static int calculateCapacity(

Object[] elementData, int minCapacity) {

if (elementData ==

DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

return Math.max(DEFAULT_CAPACITY, minCapacity);

}

return minCapacity;

}

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(

calculateCapacity(elementData, minCapacity)

);

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;

// overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

🎍扩容方法


/**

  • 数组可以分配的最大size

  • 一些虚拟机在数组中预留一些header words

  • 如果尝试分配更大的size,可能导致OutOfMemoryError

*/

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

/**

  • 增加容量,至少保证比minCapacity大

  • @param minCapacity 期望的最小容量

*/

private void grow(int minCapacity) {

// 有可能溢出的代码

int oldCapacity = elementData.length;

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);

}

/**

  • 最大容量返回 Integer.MAX_VALUE

*/

private static int hugeCapacity(int minCapacity) {

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return (minCapacity > MAX_ARRAY_SIZE) ?

Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

通常情况新容量是原来容量的1.5倍

如果原容量的1.5倍比minCapacity小,那么就扩容到minCapacity

特殊情况扩容到Integer.MAX_VALUE

看完构造方法、添加方法、扩容方法之后,我们发现,原来,new ArrayList()会将elementData 赋值为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,new ArrayList(0)会将elementData 赋值为 EMPTY_ELEMENTDATAEMPTY_ELEMENTDATA添加元素会扩容到容量为1,而DEFAULTCAPACITY_EMPTY_ELEMENTDATA扩容之后容量为10

通过反射我们可以验证这一想法。如下:

public static void main(String[] args) {

printDefaultCapacityList();

printEmptyCapacityList();

}

public static void printDefaultCapacityList() {

ArrayList defaultCapacity = new ArrayList();

System.out.println(

“default 初始化长度:” + getCapacity(defaultCapacity));

defaultCapacity.add(1);

System.out.println(

“default add 之后 长度:” + getCapacity(defaultCapacity));

}

public static void printEmptyCapacityList() {

ArrayList emptyCapacity = new ArrayList(0);

System.out.println(

“empty 初始化长度:” + getCapacity(emptyCapacity));

emptyCapacity.add(1);

System.out.println(

“empty add 之后 长度:” + getCapacity(emptyCapacity));

}

public static int getCapacity(ArrayList<?> arrayList) {

Class arrayListClass = ArrayList.class;

try {

// 获取 elementData 字段

Field field = arrayListClass.getDeclaredField(“elementData”);

// 开启访问权限

field.setAccessible(true);

// 把示例传入get,获取实例字段elementData的值

Object[] objects = (Object[]) field.get(arrayList);

//返回当前ArrayList实例的容量值

return objects.length;

} catch (Exception e) {

e.printStackTrace();

return -1;

}

}

🎨移除方法


移除指定下标元素方法

/**

  • 移除列表中指定下标位置的元素

  • 将所有的后续元素,向左移动

  • @param 要移除的指定下标

  • @return 返回被移除的元素

  • @throws 下标越界会抛出IndexOutOfBoundsException

*/

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);

// 将引用置空,让GC回收

elementData[–size] = null;

return oldValue;

}

移除指定元素方法

/**

  • 移除第一个在列表中出现的指定元素

  • 如果存在,移除返回true

  • 否则,返回false

  • @param o 指定元素

*/

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);

// 将引用置空,让GC回收

elementData[–size] = null;

}

🥽查找方法


查找指定元素的所在位置

/**

  • 返回指定元素第一次出现的下标

  • 如果不存在该元素,返回 -1

  • 如果 o ==null 会特殊处理

*/

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;

}

查找指定位置的元素

/**

  • 返回指定位置的元素

  • @param index 指定元素的位置

  • @throws index越界会抛出IndexOutOfBoundsException

*/

public E get(int index) {

rangeCheck(index);

return elementData(index);

}

该方法直接返回elementData数组指定下标的元素,效率还是很高的。所以ArrayList,for循环遍历效率也是很高的。

🎉序列化方法


/**

  • 将ArrayLisy实例的状态保存到一个流里面

*/

private void writeObject(java.io.ObjectOutputStream s)

throws java.io.IOException{

// Write out element count, and any hidden stuff

int expectedModCount = modCount;

s.defaultWriteObject();

// Write out size as capacity for behavioural compatibility with clone()

s.writeInt(size);

// 按照顺序写入所有的元素

for (int i=0; i<size; i++) {

s.writeObject(elementData[i]);

}

if (modCount != expectedModCount) {

throw new ConcurrentModificationException();

}

}

🥏反序列化方法


/**

  • 根据一个流(参数)重新生成一个ArrayList

*/

private void readObject(java.io.ObjectInputStream s)

throws java.io.IOException, ClassNotFoundException {

elementData = EMPTY_ELEMENTDATA;

// Read in size, and any hidden stuff

s.defaultReadObject();

// Read in capacity

s.readInt();

if (size > 0) {

// be like clone(), allocate array based upon size not capacity

ensureCapacityInternal(size);

Object[] a = elementData;

// Read in all elements in the proper order.

for (int i=0; i<size; i++) {

a[i] = s.readObject();

}

}

}

看完序列化,反序列化方法,我们明白:elementData之所以用transient修饰,是因为JDK不想将整个elementData都序列化或者反序列化,而只是将size和实际存储的元素序列化或反序列化,从而节省空间和时间

🩰创建子数组


public List subList(int fromIndex, int toIndex) {

subListRangeCheck(fromIndex, toIndex, size);

return new SubList(this, 0, fromIndex, toIndex);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值