简介
ArrayList就是动态数组,数组元素的类型为Object类型,对ArrayList的所有操作底层都是基于数组的;实现了ICollection和IList接口,路径为java.util包下
ArrayList创建方式
List<Object> list = new ArrayList<>();
List<Object> list1 = new ArrayList<>(40);
是否线程安全
ArrayList在多线程的环境下是线程不安全的,ArrayList进行添加元素的操作的时候是分两个步骤进行的,即第一步将size的值增加1,第二步在object[size]的位置上存放需要添加的元素;由于这个过程在多线程的环境下是不能保证具有原子性的,因此ArrayList在多线程的环境下是线程不安全的。
如何保证线程安全呢?
- 对使用ArrayList的方法进行synchronized修饰
- List<String> data=Collections.synchronizedList(new ArrayList<>());
多说一下: ArrayList与LinkedList都实现了List的接口,用法一样,但使用场景有所不同,ArrayList适用于大数据量经常查询时,LinkedList 适用于在表中进行插入、删除时使用,两者都是非线程安全的。
继承关系
来自网络继承层次
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
成员变量
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
//默认的初始化容量
//当ArrayList的构造方法中没有显示指出ArrayList的数组长度时,类内部使用默认缺省时对象数组的容量大小,为10
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
//空数组,会在构造函数中给予0参数的情况下,赋值给elementData
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 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.
*/
//当ArrayList的构造方法中没有显示指出ArrayList的数组长度时,类内部使用默认缺省时对象数组为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 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.
*/
//ArrayList的底层数据结构,只是一个对象数组,用于存放实际元素,并且被标记为transient,也就意味着在序列化的时候此字段是不会被序列化的
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
//实际ArrayList中存放的元素的个数,默认时为0个元素
private int size;
/**
* 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
*/
//ArrayList中的对象数组的最大数组容量
//有些虚拟机在数组中保留一些头字。尝试分配较大的数组可能会导致内存溢出。所以-8了
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造函数
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
* 带有初始化容量的构造参数
*/
public ArrayList(int initialCapacity) {
//初始化容量大于0
if (initialCapacity > 0) {
// 初始化一个initialCapacity大小的Object数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 初始化容量等于0,则使用默认的空数组赋值
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 初始化容量小于0,抛出非法容量异常
throw new IllegalArgumentException("Illegal Capacity: " +
initialCapacity);
}
}
/**
* Constructs an empty list with an initial capacity of ten.
* 无参构造
*/
public ArrayList() {
//赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
//而不是EMPTY_ELEMENTDATA,表示的意思就是当add的时候,会默认扩容为DEFAULT_CAPACITY 也就是10
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 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) {
// 1.将参数中的集合转化为数组赋值给elementData
elementData = c.toArray();
//2.比较参数集合是否为空,如果不为空
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
//判断是否成功将参数集合转换为Object数组,如果转换成功,则将数组进行复制,转换为Object类型的数组
if (elementData.getClass() != Object[].class) {
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
} else {
// replace with empty array.
// 如果为空,则设置元素数组为空,将EMPTY_ELEMENTDATA赋值给elementData
this.elementData = EMPTY_ELEMENTDATA;
}
}
add操作
/**
* 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;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 若 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
// 则代表调用默认无参构造函数,第一次add,如果最小容量小于10,
// 则默认扩容大小为DEFAULT_CAPACITY=10,如果大于,则以最小容量扩容
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//Math.max(a,b) a>b?a:b 取最大值
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
//迭代器
modCount++;
//最小容量如果大于数组实际长度,则进行扩容
//否则不扩容
// overflow-conscious code
if (minCapacity - elementData.length > 0) {
grow(minCapacity);
}
}
/**
* 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;
// 新的容量=旧容量+旧容量/2 也就是说新扩容大小为旧容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩容1.5倍之后,还是小,那么以最小的容量进行扩容
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);
}
private static int hugeCapacity(int minCapacity) {
// 如果minCapacity 小于0,有点奇怪,什么时候会小于0?接着说小于0时,抛出内存溢出异常
if (minCapacity < 0) // overflow
{
throw new OutOfMemoryError();
}
//如果最小容量大于最大容量时,直接扩容为Integer.MAX_VALUE 也就是是2的31次方 2147483647
//这里表示将最大容量扩展为Integer.MAX_VALUE,那么MAX_ARRAY_SIZE还有什么意义呢?
//这里minCapacity没有溢出说明是小于MAX_ARRAY_SIZE,但是分为两种情况,如果小于MAX_ARRAY_SIZE
//那么就直接扩容为MAX_ARRAY_SIZE
//否则比如说是MAX_VALUE-2,那么最多扩容为MAX_VALUE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
扩容机制
ArrayList的扩容主要发生在向ArrayList集合中添加元素的时候。由add()方法的分析可知添加前必须确保集合的容量能够放下添加的元素。主要经历了以下几个阶段:
第一:add()方法中调用ensureCapacityInternal(size + 1)方法来确定集合确保添加元素成功的最小集合容量minCapacity的值。参数为size+1,代表的含义是如果集合添加元素成功后,集合中的实际元素个数。换句话说,集合为了确保添加元素成功,那么集合的最小容量minCapacity应该是size+1。在ensureCapacityInternal方法中,首先判断elementData是否为默认的空数组,如果是,minCapacity为minCapacity与集合默认容量大小中的较大值。
第二:调用ensureExplicitCapacity(minCapacity)方法来确定集合为了确保添加元素成功是否需要对现有的元素数组进行扩容。首先将结构性修改计数器加一;然后判断minCapacity与当前元素数组的长度的大小,如果minCapacity比当前元素数组的长度的大小大的时候需要扩容,进入第三阶段。
第三:如果需要对现有的元素数组进行扩容,则调用grow(minCapacity)方法,参数minCapacity表示集合为了确保添加元素成功的最小容量。在扩容的时候,首先将原元素数组的长度增大1.5倍(oldCapacity + (oldCapacity >> 1)),然后对扩容后的容量与minCapacity进行比较:① 新容量小于minCapacity,则将新容量设为minCapacity;②新容量大于minCapacity,则指定新容量
注意:当新容量小于最大容量时,则直接用最大容量,新容量大于最大容量时,直接为Interger.MAX_VALUE,也就是2的31次方2147483647
最后将旧数组拷贝到扩容后的新数组中。
get操作
/**
* 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);
}
/**
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does *not* check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an ArrayIndexOutOfBoundsException if index is negative.
*/
private void rangeCheck(int index) {
// 如果下标数量大于元素数量,则抛出越界异常
if (index >= size) {
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
Set操作
/**
* Replaces the element at the specified position in this list with
* the specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
// 检查是否合法的索引(是否越界)
rangeCheck(index);
// 旧值
E oldValue = elementData(index);
//赋新值
elementData[index] = element;
// 返回旧值
return oldValue;
}
remove操作
/**
* 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);
// remove 和add modCount加1
modCount++;
//取要删除的值
E oldValue = elementData(index);
//保留将要被移除的元素,将移除位置之后的元素向前挪动一个位置,将list末尾元素置空(null),返回被移除的元素。
int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
}
// 赋值为空,有利于进行GC
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
/**
* 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) {
// 移除此列表中首次出现的指定元素(如果存在)。这是应为ArrayList中允许存放重复的元素。
// 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。
if (o == null) {
for (int index = 0; index < size; index++) {
if (elementData[index] == null) {
//类似remove(int index),移除列表中指定位置上的元素。
fastRemove(index);
return true;
}
}
} else {
// 不为null的元素,移除第一个出现的,同remove(index)一样,移除之后的元素向前挪一个位置,将最后一个元素置空
for (int index = 0; index < size; index++) {
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
}
return false;
}
IndexOf 操作
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
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;
}
}
}
//没有找到,则返回-1
return -1;
}
ArrayList的优缺点
优点
ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了RandomAccess接口,因此查找也就是get的时候非常快。
ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素而已。
根据下标遍历元素,效率高。
根据下标访问元素,效率高。
可以自动扩容,默认为每次扩容为原来的1.5倍。
缺点
插入和删除元素的效率不高。
根据元素下标查找元素需要遍历整个元素数组,效率不高。
线程不安全。
网友评论