创建
List<String> list = new ArrayList<>();
这是我们熟悉的创建方法,进去看看。
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
代码很简单,elememtData其实就是用来存储插入的元素的。可以看到,默认的创建方式elementData指向了一个空数组,也就是说ArrayList默认大小为0。(注意这是jdk1.8的源码,jdk1.8以前默认大小为10,感兴趣的朋友可以看看这篇文章),再看看另外一种创建方式
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
代码也很简单,如果传进来的是非空集合,把这个集合的所有元素拷贝下来,如果是空集合,处理方式就和第一种方法一样。
插入元素
插入元素也有多种方式,先看最基本的。
list.add("str0");
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
可以看到,在真正插入之前要对容量进行检查。跳进ensureCapacityInternal方法看看
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
...
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
...
再跳进ensureExplicitCapacity方法看看
private void ensureExplicitCapacity(int minCapacity) {
//容器改动次数加一
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
minCapacity其实就是插入操作所需容量的最小值(回到上面的代码可以知道传进来的是当前元素个数+1)。接下来做一个判断,其实就是判断插入后会不会溢出,如果会就跳进grow方法进行扩容,看看grow方法。
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//1
int newCapacity = oldCapacity + (oldCapacity >> 1);
//2
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//3
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);
}
...
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
...
代码1:计算原容量的1.5倍是多少(为什么不用浮点数乘?因为在汇编代码里浮点数乘的操作要比移位和加减操作复杂很多)。
代码2:如果扩大了1.5倍还是不够,就干脆等于所需容量最小值。
代码3:如果新容量值超出MAX_ARRAY_SIZE,那么这个时候实际上已经接近溢出的边缘了(MAX_ARRAY_SIZE 仅仅比 Integer.MAX_VALUE 小8),跳进hugeCapacity方法。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
从代码可以看出ArrayList最大容量为Integer.MAX_VALUE,不能再多了。至此,插入前的容量检查工作就完成了,返回到原来的位置继续分析。
```java
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal方法检查容量没问题后,就可以安心地插入新元素了,源码里并没有进行判空,也就是说add方法是可以插入空值的。再看看另一种插入方式。
list.add(5,"str0");
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
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++;
}
在真正插入之前,先检查一下插入位置是否合法。
/**
* A version of rangeCheck used by add and addAll.
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
代码很简单,不合法就抛出异常。
检查没问题后插入即可。
查询
因为元素是使用数组来存储的,所以查询操作就很简单了。
list.get(0);
先检查一下参数是否合法,再查询。
...
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
...
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
...
删除
按下标删除
list.remove(5);
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
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;
}
先进行参数检查,把删除后的所有元素向前移一步。再看看按元素删除。
list.remove("str0");
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
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;
}
ArrayList是允许插入空值的。这种删除操作是把容器数组遍历一遍,删除指定的值。看看fastRemove方法
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
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
}
很简单,其实就是数组移位覆盖操作。
其他方法
trimToSize方法
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
从注释可以知道这个方法可以在内存短缺的时候调用,释放一些多余的空间。注意size和elementData.length的区别,size是元素的个数,elementData.length是容器的大小。
ArrayList在进行了多次增、删、扩容操作后,最后可能只剩下了几个元素,但数组容量却被扩的很大,大量空白空间留着不用是很浪费的,这个方法就是用来减少不必要空间开支的。
线程安全问题
倒回去再看看ArrayList增删元素的源码,会发现它们并没有上锁解锁操作,也就是说ArrayList是线程不安全的。