ArrayList
java version “1.8.0_131”
一、总结
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*/
private transient Object[] elementData;
- 底层实现是一个Object类型的数组,数组不可序列化
二、源码分析
1.toArray()
将集合转化为数组;将数组转为集合Arrays.asList();
/**
* Returns an array containing all of the elements in this list
* in proper sequence (from first to last element).
*
* <p>The returned array will be "safe" in that no references to it are
* maintained by this list. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
*
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this list in
* proper sequence
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
方法注释
- 以适当的顺序返回包含该列表中所有元素的数组(从第一个元素到最后一个元素)。即按照集合中的元素顺序,即转化后的数组与原集合中的元素具有相同的顺序。
- 该方法返回的数组是安全的,因为没有引用指向它(因为ArrayList内部的数据结构为数组,返回的是其内部属性数组的copy副本),完全由当前的集合维护;换句话说,此方法一定分配创建一个新的数组。
- 此方法的调用者可以随便更改得到的数据,而不会影响原来的集合中的内容;
方法中的参数
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
// ArrayList 中包含的元素个数,即 ArrayList 的 size
private int size;
成员变量的注释
- size : ArrayList 中包含的元素个数,而不是数组的长度
- elementData : ArrayList 中的元素存储在此数据缓冲区中,ArrayList 的容量就是此数据缓冲区的长度,任何空的 ArrayList 赋值为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ;当加入第一个元素后,数组容量将被扩充至10;transient 修饰,不可序列化
DEFAULTCAPACITY_EMPTY_ELEMENTDATA 默认的空的Object[] 数组
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
注意
- 该方法的返回值为Object[] 类型数组,强制转为为T[]会抛出转化异常;只能在使用时再对Object[]数组中的元素进行转换
// 使用示例
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
Object[] array2 = integerList.toArray();
for(Object obj : array2){
System.out.println((Integer)obj);
}
方法实现调用 Arrays.copy()
/**
* Returns an array containing all of the elements in this list in proper
* sequence (from first to last element); the runtime type of the returned
* array is that of the specified array. If the list fits in the
* specified array, it is returned therein. Otherwise, a new array is
* allocated with the runtime type of the specified array and the size of
* this list.
*
* <p>If the list fits in the specified array with room to spare
* (i.e., the array has more elements than the list), the element in
* the array immediately following the end of the collection is set to
* <tt>null</tt>. (This is useful in determining the length of the
* list <i>only</i> if the caller knows that the list does not contain
* any null elements.)
*
* @param a the array into which the elements of the list are to
* be stored, if it is big enough; otherwise, a new array of the
* same runtime type is allocated for this purpose.
* @return an array containing the elements of the list
* @throws ArrayStoreException if the runtime type of the specified array
* is not a supertype of the runtime type of every element in
* this list
* @throws NullPointerException if the specified array is null
*/
@SuppressWarnings("unchecked")
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;
}
方法注释
- 按照集合中的元素的顺序(从第一个到最后一个的顺序)返回一个包含该集合中所有元素的数组;方法返回的运行时的数组类型是传入的参数的数组类型;
- 如果传入的数组的长度与集合中元素的个数相匹配,则将集合中的元素放入该数组中;否则会根据传入的参数的数组类型按照集合中元素的个数重新创建一个数组,存放集合中的元素
- 如果传入的数组的长度大于集合中元素的个数,则在将集合中元素拷贝到传入的数组中后,将数组中多余的位置设置为NULL;如果调用方知道数据中不包含任何NULL元素,这样处理可以方便的知道数组中元素的个数
方法测试
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
// 添加 3 个元素
Object[] array2 = integerList.toArray();
for(Object obj : array2){
System.out.println((Integer)obj);
}
// 1.若 new Integer[2] 2 < 3 integerArrays 中无元素,即当 传入数组的长度小于集合中元素的个数时,重新生成新的数组返回
// 2.若 new Integer[3] 3 = 3 integerArrays 中有元素
// 3.若 new Integer[5] 5 > 3 integerArrays 中有元素且从下标3以后,元素为null;即传入数组的长度大于集合中元素的个数时的情况;
// 4.方法注释中说仅当调用方知道集合中无NULL时,传入数组的长度大于集合中元素的个数时,设置copy元素后的位置为NULL,有利于确认集合的长度
Integer[] integerArrays = new Integer[5];
Integer[] array = integerList.toArray(integerArrays);
for(Integer inte : integerArrays){
System.out.println(inte+" a");
}
for(Integer inte : array){
System.out.println(inte+" b");
}
System.out.println(array.length);
}
注意
- 传入的数组,大小要与转化后的数组中的元素个数相同,可避免底层的多余判断处理
// 正确调用代码示例
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
2.retainAll() 、removeAll()
- retainAll() 取交集
- removeAll() 取差集
/**
* Retains only the elements in this list that are contained in the
* specified collection. In other words, removes from this list all
* of its elements that are not contained in the specified collection.
*
* @param c collection containing elements to be retained in this list
* @return {@code true} if this list changed as a result of the call
* @throws ClassCastException if the class of an element of this list
* is incompatible with the specified collection
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
public boolean retainAll(Collection<?> c) {
return batchRemove(c, true);
}
/**
* Removes from this list all of its elements that are contained in the
* specified collection.
*
* @param c collection containing elements to be removed from this list
* @return {@code true} if this list changed as a result of the call
* @throws ClassCastException if the class of an element of this list
* is incompatible with the specified collection
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false);
}
// retainAll 与 removeAll 调用方法相同
// retainAll 需要保留参数中含有的内容
// retainAll 不需保留参数中含有的内容
private boolean batchRemove(Collection<?> c, boolean complement) {
// A.retainAll(B)
// 此处是A的集合内容
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
// 如果 B 中包含A中的元素,且为交集,=true
// 则改变 A 元素,即虽然方法的返回值是Boolean类型
// 但实际上集合 A 的内容已经改变
// 如果调用此方法依然要使用到原来的A集合,调用方法时使用A的克隆
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
// 当 B 集合调用过程中发生空指针异常,说明集合B是空的
// r = w = 0
// 在 elementData 的 0 开始拷贝 elementData 从 0 开始的 size - r 的长度的元素,即原来的 elementData
// 此处正常运行,
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
// w 位置后的元素为不需要的元素,设置为NULL 等待GC 回收
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
// 如果B集合为空,运行到此处抛出异常NPE
// 因为该方法中并未捕获处理异常
}
}
return modified;
}
public static void main(String[] args) {
// w != size
List<Integer> a = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
List<Integer> b = new ArrayList<>(Arrays.asList(3,4,5,6));
a.retainAll(b);
System.out.println(a);
// r != size
List<Integer> c = null ;
c.add(1);
c.add(2);
a.retainAll(c);
}