public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
private static final long serialVersionUID = 8683452581122892189L;
//序列号
注:关于序列化:
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
ObjectOutputStream:代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream:代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
//指定为0时返回的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//默认返回的空数组
transient Object[] elementData;
--------------------------------------------------------------------------------------------------------
关于它的构造函数:
public ArrayList(int initialCapacity) {
//传入一个整型的首容量.
if (initialCapacity > 0) {
//首容量>0则建立一个首容量大小的object数组
this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {
//首容量==0,建立一个EMPTY_ELEMENTDATA = {}的空数组
this.elementData = EMPTY_ELEMENTDATA;} else {
//要是首容量<0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
}
}
----------------------------------------------------------------------------------------------------------
public ArrayList() {
//生成一个临时的object数组并赋空。
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
-----------------------------------------------------------------------------------------------------------
注:关于class和getclass的区别
java有两个获得类名的方法getClass()和class(),这两个方法看似一样,实则不然。这两个方法涉及到了java中的反射。
反射:在运行时期获取对象类型信息的操作。(在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代码)
类型类 :在Java中一切都是对象,一般所使用的对象都直接或间接继承自Object类。Object类中包含一个方法名叫getClass,利用这个方法就可以获得一个实例的类型类。类型类指的是代表一个类型的类,因为一切皆是对象,类型也不例外,在Java使用类型类来表示一个类型。所有的类型类都是Class类的实例。
getClass()是一个类的实例所具备的方法,而class()方法是一个类的方法。 另外getClass()是在运行时才确定的,而class()方法是在编译时就确定了。
public ArrayList(Collection<? extends E> c) {
//传入一个collection集合,将集合转化为一个临时的数组。
elementData = c.toArray();
if ((size = elementData.length) != 0) {
//给size赋值,并判断size不为0
// c.toArray might (incorrectly) not return Object[] (see 6260652)
//toArray方法不一定返回一个object数组
if (elementData.getClass() != Object[].class)
//判断c.toArray()是否返回了一个object类型的数组,如果没有,用 public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)方法得到一个object的数组。
elementData = Arrays.copyOf(elementData, size, Object[].class);} else {
// replace with empty array.如果size为0,就直接返回一个空的object的数组this.elementData = EMPTY_ELEMENTDATA;
}
}
----------------------------------------------------------------------------------------------------
其他方法:
将数组缓冲区大小调整到实际 ArrayList 存储元素的大小,即 elementData = Arrays.copyOf(elementData, size);
该方法由用户手动调用,以减少空间资源浪费的目的
public void trimToSize() {
modCount++;
// modCount 是 AbstractList 的属性值:protected transient int modCount = 0;
// 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的
// 当实际大小 < 数组缓冲区大小时
// 如调用默认构造函数后,刚添加一个元素,此时 elementData.length = 10,而 size = 1
// 通过这一步,可以使得空间得到有效利用,而不会出现资源浪费的情况if (size < elementData.length) {
elementData = (size == 0)? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
//如果size==0成立就付给elementData一个空数组,不成立就给elementData赋一个size大小的数组,由此调整数组的缓冲区大小
}}
---------------------------------------------------------------------------------------------------------
指定 ArrayList 的容量
//minCapacity 指定的最小容量
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0 : DEFAULT_CAPACITY;
//传入一个最小容量,判断elementData是否为空数组,是最小扩容为0,否则为10
if (minCapacity > minExpand) {
//如果最小容量大于最小扩容执行此方法
ensureExplicitCapacity(minCapacity);}
}
----------------------------------------------------------------------------------------------------
用于计算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果elementData数组为空
return Math.max(DEFAULT_CAPACITY, minCapacity);
//判断10和传入的最小容量,并返回最大值
}
//数组不为空,就返回最小容量
return minCapacity;}
--------------------------------------------------------------------------------------------------
明确 ArrarList 的容量,提供给本类使用的方法 , 用于内部优化,保证空间资源不被浪费:尤其在 add() 方法添加时起效private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
---------------------------------------------------------------------------------------------------
私有方法:明确 ArrayList 的容量
用于内部优化,保证空间资源不被浪费:尤其在 add() 方法添加时起效
private void ensureExplicitCapacity(int minCapacity) {
//传入最小容量
modCount++;
// 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的
// overflow-conscious code( 防止溢出代码:确保指定的最小容量 > 数组缓冲区当前的长度)
if (minCapacity - elementData.length > 0)
//最小容量大于elementData数组的长度,就用grow()扩容
grow(minCapacity);}
-----------------------------------------------------------------------------------------------------------
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//public static final int MAX_VALUE = 0x7fffffff;(
-----------------------------------------------------------------------------------------------------------
扩容:
注:关于移位:(将数值化为二进制,向左或向右移规定的位数,然而odlcapacity>>1就相当于移了0.5倍
数组缓冲区最大存储容量:
一些 VM 会在一个数组中存储某些数据。要减去 8 的原因 : 尝试分配这个最大存储容量,可能会导致 OutOfMemoryError(当该值 > VM 的限制时) 。
private void grow(int minCapacity) {
// overflow-conscious code
//传入一个最小容量
int oldCapacity = elementData.length;
//一个旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
//新容量是旧容量的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//新容量等于指定容量和旧容量中更大的一方
if (newCapacity - MAX_ARRAY_SIZE > 0)
//如果新容量大于一个int型所能表示的最大数值-8,就调用hugeCapacity()方法
newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:
//如果可以就直接生成一个新容量的object数组
elementData = Arrays.copyOf(elementData, newCapacity);}
------------------------------------------------------------------------------------------------------------
大容量分配,最大分配 Integer.MAX_VALUEprivate static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
//传入的最小扩容<0,就抛出异常
throw new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
//如果最小容量大于一个int型所能表示的最大数值-8,返回一个int型能表示的最大值,否则返回一个int型所能表示的最大数值-8
}
-------------------------------------------------------------------------------------------------------------
关于输入最小扩容,进行扩容操作的逻辑总结:
进行自己设置的最小容量 的扩容操作,要用public void ensureCapacity(int minCapacity)方法(其他的都是私有方法,用来完善你的扩容操作),首先传入一个最小扩容量,开始判断你设置的ArrayList的对象是否是默认的空数组,是则说明当前分配容量为0,否则表面当前分配容量为10,如果你设置的最小容量小于0或10,就去调用 private void ensureExplicitCapacity(int minCapacity) 来进行扩容操作,要是满足最小容量大于当前数组长度,就进行扩容,判断最小扩容和自动扩容的大小,可能是以1.5倍增加,也可以是直接以最小扩容增加,(最大扩容:存在生成一个int型的最大扩容)生成一个扩容后的object数组。
。
-------------------------------------------------------------------------------------------------------------
获取该 list 所实际存储的元素个数
public int size() {
return size;
}
------------------------------------------------------------------------------------------------------------
判断 list 是否为空
public boolean isEmpty() {
return size == 0;}
-------------------------------------------------------------------------------------------------------------
判断是否存在一个object的对象
public boolean contains(Object o) {
//传回来的值如果是>=0就证明找到了相应的下标,否则,没有找到
return indexOf(o) >= 0;}
--------------------------------------------------------------------------------------------------------------
顺序查找,返回元素的最低索引值(最首先出现的索引位置)
if (o == null) {
//o为空,就去找集合里找一个null元素,并返回下标位置(注意:元素为 null 并非表示这是非法操作。空值也可以作为元素放入 ArrayList)
for (int i = 0; i < size; i++)
//顺序查找数组缓冲区。注意:Arrays 工具类提供了二分搜索,但没有提供顺序查找
if (elementData[i]==null)return i;
} else {
//o不为空就通过循环,找和o内容相等的元素,并返回下标
for (int i = 0; i < size; i++)if (o.equals(elementData[i]))
return i;
}
//未找到,返回-1
return -1;}
---------------------------------------------------------------------------------------------------------------
反向查找(从数组末尾向开始查找),返回元素的最高索引值
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
// 逆序查找
if (elementData[i]==null)return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
-------------------------------------------------------------------------------------------------------------------
很明显,就是indexOf()和lastindexOf()都只能执行一次,也就是找到相同内容即返回,所以并没有找出所有的相同元素,而是找到了相对方法中最近的一个,区别o==null和o.eauals()方法,表示null的哈希和内容都相等。
-------------------------------------------------------------------------------------------------------------------
public Object clone() {
try {
// Object 的克隆方法:会复制本对象及其内所有基本类型成员和 String 类型成员,但不会复制对象成员、引用对象 (原因是Arrays.copyOf()的方法
ArrayList<?> v = (ArrayList<?>) super.clone();
// protected native Object clone() throws CloneNotSupportedException;
// 对需要进行复制的引用变量,进行独立的拷贝:将存储的元素移入新的 ArrayList 中v.elementData = Arrays.copyOf(elementData, size);
//v是ArrayList的对象,elementData是对象内存放的object数组名,所以要调用
v.modCount = 0;return v;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
---------------------------------------------------------------------------------------------------------------
返回 ArrayList 的 Object 数组
包含 ArrayList 的所有储存元素
对返回的该数组进行操作,不会影响该 ArrayList(相当于分配了一个新的数组)==>该操作是安全的
元素存储顺序与 ArrayList 中的一致
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
-------------------------------------------------------------------------------------------------------------------
返回 ArrayList 元素组成的数组
* @param a 需要存储 list 中元素的数组
* 若 a.length >= list.size,则将 list 中的元素按顺序存入 a 中,然后 a[list.size] = null, a[list.size + 1] 及其后的元素依旧是 a 的元素
* 否则,将返回包含list 所有元素且数组长度等于 list 中元素个数的数组
* 注意:若 a 中本来存储有元素,则 a 会被 list 的元素覆盖,且 a[list.size] = null
* @return
* @throws ArrayStoreException 当 a.getClass() != list 中存储元素的类型时
* @throws NullPointerException 当 a 为 null 时
*/
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
----------------------------------------------------------------------------------------------------------------
ArrayList提供了一个将List转为数组的一个非常方便的方法toArray。toArray有两个重载的方法:
(1)list.toArray();
(2)list.toArray(T[] a);
对于第一个重载方法,是将list直接转为Object[] 数组;
第二种方法是将list转化为你所需要类型的数组,当然我们用的时候会转化为与list内容相同的类型。
----------------------------------------------------------------------------------------------------------------