简介 ArrayList
底层数据结构是动态数组,与普通的数组相比,它的容量可以动态增长。在添加的元素数量达到一定值时,会触发自动扩容机制,保证集合的可用性。
它继承了AbstractList
抽象类,并实现了List
、RandomAccess
、Cloneable
和 java.io.Serializable
接口。
AbstractList
抽象类已经实现了List
接口,为什么ArrayList
还要去实现List
接口?
由于底层由数组存储元素,其取指定下标位置元素、在集合尾部插入元素和求数组长度的时间复杂度为O(1)
;而在指定索引位置插入和删除元素的时间复杂度为O(n)
。
使用分析 在开发中,我们会经常使用ArrayList
来存储对象,例如对数据库批量插入/修改的入参实体集合、数据库的列表查询结果集转换成前端视图模型对象等。一般来说,我们的使用形式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 List<String> list = new ArrayList <>(); list.add("a" ); list.add("b" ); list.add("c" ); list.add("d" ); list.add("e" ); list.add("f" ); list.add("g" ); list.add("h" ); list.add("i" ); list.add("j" ); list.add("k" );
这样使用似乎没有任何问题,但这却不是一个匠心程序员该写出来的代码。
为什么这么说呢?我们知道ArrayList
底层是动态数组,那这个数组什么时候进行动态呢?换句话说,数组什么时候会进行扩容?又扩容到多少呢?
以上代码执行完后到底有没有进行扩容?这些都是问题,让我们带着这些问题来看下面的源码。
源码详解 成员变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 private static final long serialVersionUID = 8683452581122892189L ;private static final int DEFAULT_CAPACITY = 10 ;private static final Object[] EMPTY_ELEMENTDATA = {};private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};transient Object[] elementData;private int size;private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ;
构造器 无参构造 构造一个初始容量为10
的空列表。将DEFAULTCAPACITY_EMPTY_ELEMENTDATA
空列表赋给存储元素的elementData
数组缓冲区。
1 2 3 public ArrayList () { this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
有参构造 1
、 构造一个具有指定初始容量的空列表。入参initialCapacity
为列表的指定初始容量,如果为负数则抛出IllegalArgumentException
异常。
1 2 3 4 5 6 7 8 9 10 11 12 public ArrayList (int initialCapacity) { if (initialCapacity > 0 ) { this .elementData = new Object [initialCapacity]; } else if (initialCapacity == 0 ) { this .elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException ("Illegal Capacity: " + initialCapacity); } }
2
、 构造一个包含指定集合元素的列表,其顺序由集合的迭代器返回。入参c
集合中的元素将被放入此列表,如果集合为null
则抛出NullPointerException
异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 public ArrayList (Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0 ) { if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this .elementData = EMPTY_ELEMENTDATA; } }
数组拷贝方法 由于ArrayList
底层使用数组进行元素存储,其很多实现都是对数组进行直接操作。所以在看其它方法之前,很有必要先弄懂一些数组的方法。
java.util.Arrays
是JDK
提供的一个数组工具类,在ArrayList
中大量使用了它的一个静态方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public static <T> T[] copyOf(T[] original, int newLength) { return (T[]) copyOf(original, newLength, original.getClass()); } public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T []> newType) { @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object [newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0 , copy, 0 , Math.min(original.length, newLength)); return copy; } public static native void arraycopy (Object src, int srcPos, Object dest, int destPos, int length) ;
插入元素 ArrayList
提供了两个重载的add
插入方法。第一个是将指定元素添加至列表末尾;第二个是将指定元素添加至指定位置;同时还提供了两个重载的addAll
方法,用来批量插入。
插入至列表末尾 源码如下:
1 2 3 4 5 public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; }
看到这里我们可以对使用分析
中的示例代码进行分析了:
首先使用new
关键字调用无参构造器初始化一个ArrayList
集合对象,我们知道其实质是将DEFAULTCAPACITY_EMPTY_ELEMENTDATA
空列表赋给存储元素的elementData
数组缓冲区。创建出的list
对象的size
成员变量初始化为0
,然后调用list
对象的add
方法添加元素。
第一次调用:list.add("a");
在add
方法中首先调用ensureCapacityInternal
方法,确保内部容量,然后将传入的元素赋值给elementData
数据域末尾。最后返回true
。
确保内部容量ensureCapacityInternal
方法源码如下:
1 2 3 private void ensureCapacityInternal (int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
第一次调用add
方法时:elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
(构造器中初始化),minCapacity = size + 1
(其值为1
)。
调用calculateCapacity(elementData, minCapacity)
方法计算容量:
1 2 3 4 5 6 7 private static int calculateCapacity (Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
此时elementData
等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,返回DEFAULT_CAPACITY
(其值为10
)和minCapacity
(其值为1
)中较大的值,即返回10
。
随即调用了ensureExplicitCapacity(10)
方法:
1 2 3 4 5 6 7 private void ensureExplicitCapacity (int minCapacity) { modCount++; if (minCapacity - elementData.length > 0 ) grow(minCapacity); }
首先增加当前集合的修改次数。然后判断计算出的容量是否超出了当前elementData
数组长度,如果超过则进行grow
扩容。
在第一次调用add
方法时显然超过了,进行扩容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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); elementData = Arrays.copyOf(elementData, newCapacity); }
扩容流程为:
获取旧容量oldCapacity
; 计算新容量,利用位运算(速度远超整除运算)得到旧容量的一半再加上旧容量,即扩容1.5
倍; 检查新容量是否小于传入的计算容量minCapacity
,如果小于,则将传入的容量作为新容量; 检查得到的新容量是否大于ArrayList
定义的所能容纳的最大容量:Integer,MAX_VALUE - 8
; 如果大于,则调用hugeCapacity(minCapacity)
计算最大容量:如果minCapacity
大于Integer.MAX_VALUE - 8
,则最大容量为Integer.MAX_VALUE
,否则为Integer.MAX_VALUE - 8
。 最后调用Arrays.copyOf(elementData, newCapacity)
进行数组拷贝,得到一个以新容量为长度的数组对象并赋值给elementData
。 第一次调用add
方法时,旧容量oldCapacity = 0
,通过位运算计算出的新容量也为0
,所以最后新容量newCapacity = minCapacity
,等于10
。
所以我们使用new
关键字调用无参构造器创建ArrayList
对象时,实际上只初始化了一个空数组,在第一次调用add
方法时才会进行空数组的扩容。
扩容完成后,elementData
数组容量充足,可以往其末尾添加元素:
1 elementData[size++] = e;
像使用分析
中的示例代码一样,我们不断地向其中添加元素,当添加的元素个数不超过10
时,ensureExplicitCapacity(int minCapacity)
方法判断minCapacity - elementData.length
始终小于0
(经过第一次扩容后elementData.length
的值为10
),不会进行grow
扩容; 而当添加至第11
个元素k
时,情况发生变化:
calculateCapacity(elementData, minCapacity)
方法直接返回minCapacity = 11
;然后调用ensureExplicitCapacity(int minCapacity)
方法,此时elementData
数组长度为10
,minCapacity - elementData.length
大于0
,将再次进行grow
扩容。 而扩容的流程我们知道,会调用Arrays.copyOf(elementData, newCapacity)
方法进行数组拷贝,会有性能损耗。
所以,具有匠心的代码应该像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 List<String> list = new ArrayList <>(11 ); list.add("a" ); list.add("b" ); list.add("c" ); list.add("d" ); list.add("e" ); list.add("f" ); list.add("g" ); list.add("h" ); list.add("i" ); list.add("j" ); list.add("k" );
调用指定初始容量的构造器,在创建list
对象时就会对elementData
数组进行初始化,而不是在第一次调用add
方法时。
所以如果能提前预估到集合容量,尽量提前指定容量,避免频繁的扩容带来的性能损耗。
根据使用场景,如果集合的数据量不好预估,且只会对其进行增删操作,则不建议使用ArrayList
集合,而是建议使用LinkedList
集合。
插入至指定位置 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void add (int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1 ); System.arraycopy(elementData, index, elementData, index + 1 , size - index); elementData[index] = element; size++; }
将一个集合全部元素插入至当前集合末尾 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public boolean addAll (Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); System.arraycopy(a, 0 , elementData, size, numNew); size += numNew; return numNew != 0 ; }
首先将传入的集合c
转为Object[]
对象数组,调用Collection
的toArray()
方法,不管是哪种集合的实现,最终都会返回一个数组。以下是ArrayList
类的实现:
1 2 3 public Object[] toArray() { return Arrays.copyOf(elementData, size); }
调用工具类的方法Arrays.copyOf
方法进行数组拷贝,返回一个Object[]
数组。
从当前集合指定索引位置开始,将一个集合全部元素插入 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public boolean addAll (int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); int numMoved = size - index; if (numMoved > 0 ) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0 , elementData, index, numNew); size += numNew; return numNew != 0 ; }
删除元素 删除集合中的元素有多种情况:删除指定索引位置元素/删除指定元素/删除指定索引范围内的元素/删除全部元素/指定条件删除(Java8
新增)等。
删除指定索引位置元素 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 ; return oldValue; }
为什么判断size - index - 1 > 0
?
答:集合大小size
是从1
开始计算,而数组下标索引index
是从0
开始计算。
删除指定元素 由于ArrayList
集合中的元素可以重复,指定的元素可能在集合中出现多次,所以该方法删除的是指定元素在集合中第一次出现位置的元素。
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 ; }
fori
循环是从索引为0
开始遍历,所以删除的是具有最低索引的元素。
我们来看下fastRemove(index);
的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 ; }
删除指定索引范围内的元素 该方法为ArrayList
类中受保护的方法,外部无法直接进行调用。由JDK
集合框架内部进行使用。
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 protected void removeRange (int fromIndex, int toIndex) { modCount++; int numMoved = size - toIndex; System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[i] = null ; } size = newSize; }
删除全部元素 有两种情况:一种是删除当前集合全部元素,方法为clear()
;另一种是从当前集合中删除指定集合中包含的所有元素,方法为removeAll(Collection<?> c)
。
clear()
方法源码如下:
1 2 3 4 5 6 7 8 9 10 11 public void clear () { modCount++; for (int i = 0 ; i < size; i++) elementData[i] = null ; size = 0 ; }
removeAll(Collection<?> c)
方法源码如下:
1 2 3 4 5 public boolean removeAll (Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false ); }
首先,调用Java8
提供的Objects.requireNonNull(c);
方法对传入的集合c
进行非空校验。
然后,调用私有方法batchRemove(c, false)
,传入集合c
和布尔值false
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private boolean batchRemove (Collection<?> c, boolean complement) { final Object[] elementData = this .elementData; int r = 0 , w = 0 ; boolean modified = false ; try { for (; r < size; r++) if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { for (int i = w; i < size; i++) elementData[i] = null ; modCount += size - w; size = w; modified = true ; } } return modified; }
我们来写一个简单的demo
来看下其过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ...... List<Integer> removeList = new ArrayList <>(); removeList.add(1 ); removeList.add(2 ); removeList.add(3 ); removeList.add(4 ); removeList.add(5 ); removeList.add(6 ); List<Integer> beRemovedList = new ArrayList <>(); beRemovedList.add(3 ); beRemovedList.add(4 ); beRemovedList.add(6 ); System.out.println(removeList); System.out.println(beRemovedList); removeList.removeAll(beRemovedList); System.out.println(removeList); ......
创建一个removeList
集合并初始化六个元素,再创建一个待删除的beRemovedList
集合并初始化三个元素(在removeList
中)。
下面我们来分析removeAll
方法的具体执行流程:
当我们调用removeList.removeAll(beRemovedList);
时,会先对beRemovedList
进行非空校验,然后调用batchRemove
方法:
1、使用局部最终变量elementData
指向当前集合(removeList
)的引用:
1 final Object[] elementData = this .elementData;
2、定义两个索引并初始化为0
,以及定义一个布尔值用来记录当前集合是否被修改:
1 2 int r = 0 , w = 0 ;boolean modified = false ;
3、从r
到size
进行遍历,判断传入的集合c
是否包含r
位置的元素。
1 2 3 for (; r < size; r++) if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r];
我们对着我们的demo
程序进行分析,c = {3, 4, 6}
,elementData = {1, 2, 3, 4, 5, 6}
,size=6
,complement = false
。
每次循环r
的值增一,循环结束的条件为r < size
不成立,即当r = size
时循环结束。
第一次循环:r = 0
,elementData[r] = 1
,c.contains(1) = false
。if
条件成立,w = 0
。将elementData[r]
赋值给elementData[w++]
:即将当前不在c
集合中的元素赋值到集合的第0
位置,随后w
增一。此时elementData
的第0
位置元素为:1
。 第二次循环:r = 1
,elementData[r] = 2
,c.contains(2) = false
。if
条件成立,w = 1
。将elementData[r]
赋值给elementData[w++]
:即将当前不在c
集合中的元素赋值到集合的第1
位置,随后w
增一。此时elementData
的第1
位置元素为:2
。 第三次循环:r = 2
,elementData[r] = 3
,c.contains(3) = true
。if
条件不成立,w
的值为2
,不做任何操作。 第四次循环:r = 3
,elementData[r] = 4
,c.contains(4) = true
。if
条件不成立,w
的值为2
,不做任何操作。 第五次循环:r = 4
,elementData[r] = 5
,c.contains(5) = false
。if
条件成立,w = 2
,将elementData[r]
赋值给elementData[w++]
:即将当前不在c
集合中的元素赋值到集合的第2
位置,随后w
增一。此时elementData
的第2
位置元素为:5
。 第六次循环:r = 5
,elementData[r] = 6
,c.contains(6) = true
。if
条件不成立,w
的值为3
,不做任何操作。 循环结束,w = 3
,elementData = {1, 2, 5, ......}
,r = 6
。
接下来我们来看下finally
块中的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (r != size) { System.arraycopy(elementData, r,elementData, w,size - r); w += size - r; } if (w != size) { for (int i = w; i < size; i++) elementData[i] = null ; modCount += size - w; size = w; modified = true ; }
对于我们的demo
程序:
此时r = size
,第一个if
块不进入。
此时(w = 3) != (size = 6)
,进入第二个if
块,将索引从w
到size - 1
位置的元素置为null
,释放对原来元素的引用。
接下来是一些收尾工作:
记录修改次数,修改(移除)了size - w
个元素; 将集合大小设为w
:为在for
循环中给elementData
域赋值的元素个数。 设置修改标记为true
,此处是c
集合中的元素全部从集合中删除。 最后,batchRemove
方法返回modified
布尔值:表示是否当前集合中删除了指定集合c
中包含的所有元素。
修改元素 修改方法只有一个:修改指定索引位置的元素为新的元素。
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public E set (int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
查询元素 由于ArrayList
底层由elementData
数组存储元素,所以支持按数组下标访问:即随机快速访问。其查询的时间复杂度为O(1)
,这也是为什么ArrayList
实现RandomAccess
的原因:标记该类支持随机快速访问。
1 2 3 4 5 6 7 8 9 10 public E get (int index) { rangeCheck(index); return elementData(index); } E elementData (int index) { return (E) elementData[index]; }
其它方法 clone
方法传送门
size
方法获取集合大小:返回成员变量size
。
isEmpty
方法判断集合是否为空集合:返回size == 0
得到的值。
indexOf
方法返回指定元素在当前集合中第一次出现的位置索引。如果当前集合中不包含此元素,返回-1
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 ; }
从前往后遍历,null
值使用==
运算符进行比较,其它对象使用equals
方法比较。
lastIndexOf
方法返回指定元素在当前集合中最后一次出现的位置索引。如果当前集合中不包含此元素,返回-1
。
1 2 3 4 5 6 7 8 9 10 11 12 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 ; }
从后往前遍历,null
值使用==
运算符进行比较,其它对象使用equals
方法比较。
contains
方法判断指定元素是否在集合中。调用的是indexOf(o)
方法,判断其返回值是否大于等于0
,等于-1
说明不在集合中。
iterator
方法该方法是迭代器设计模式的体现。使用了new
关键字创建了一个私有内部类Itr
对象。
1 2 3 4 5 6 7 8 9 10 11 12 public Iterator<E> iterator () { return new Itr (); } private class Itr implements Iterator <E> { int cursor; int lastRet = -1 ; int expectedModCount = modCount; Itr() {} ...... }
私有内部类Itr
实现了java.util.Iterator
迭代器接口。其成员变量有三个:
cursor
:游标(下一个要返回元素的索引)。lastRet
:初始化为-1
,最后一个被返回元素的索引;如果集合中本来没有任何元素则返回-1
。expectedModCount
:期望的修改次数。初始化为当前集合的modCount
。只有一个无参构造函数。
我们知道使用迭代器对ArrayList
进行遍历的写法如下:
1 2 3 4 5 6 7 8 9 10 11 12 List<Integer> iteratorList = new ArrayList <>(); iteratorList.add(1 ); iteratorList.add(2 ); iteratorList.add(3 ); iteratorList.add(4 ); iteratorList.add(5 ); iteratorList.add(6 ); Iterator<Integer> iterator = iteratorList.iterator(); while (iterator.hasNext()) { Integer next = iterator.next(); System.out.println(next); }
其中关键的两个方法为:hasNext()
和next()
。接下来我们着重来看下这两个方法。
首先调用iteratorList
对象的iterator()
方法得到一个迭代器对象,经过上面的分析我们可知这实际是一个Itr
对象。
其hasNext()
方法源码如下:
1 2 3 public boolean hasNext () { return cursor != size; }
返回当前游标cursor
是否不等于集合大小size
。如果不等于size
,说明还有下一个元素。可继续迭代。否则迭代完成。
next()
方法源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public E next () { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException (); Object[] elementData = ArrayList.this .elementData; if (i >= elementData.length) throw new ConcurrentModificationException (); cursor = i + 1 ; return (E) elementData[lastRet = i]; }
在Itr
类中使用this
指代的是当前Itr
对象,使用ArrayList.this
指代的是集合对象。
为什么ArrayList
使用迭代器遍历没有普通fori
循环遍历效率高?
答:经过以上代码的分析,原因显而易见:使用迭代器遍历,首先需要使用new
关键字创建一个Itr
对象,创建对象需要耗时(一次);其次,中间有多次条件判断并且有局部变量产生,以及一个加1
操作,这也需要耗费时间(多次:每次调用next
方法)。
Demo
实战1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 package com.sunchaser.javase.collect;import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class ArrayListTest { public static void main (String[] args) { ArrayList<String> addList = new ArrayList <>(); addList.add("测试1" ); System.out.println(addList); addList.add(1 ,"指定位置1" ); System.out.println(addList); ArrayList<String> toBeAddList = new ArrayList <>(); toBeAddList.add("测试2" ); toBeAddList.add("测试3" ); toBeAddList.add("测试4" ); ArrayList<String> toBeAddIndexList = new ArrayList <>(); toBeAddIndexList.add("测试5" ); toBeAddIndexList.add("测试6" ); addList.addAll(toBeAddList); System.out.println(addList); addList.addAll(1 ,toBeAddIndexList); System.out.println(addList); List<Integer> removeList = new ArrayList <>(); removeList.add(1 ); removeList.add(2 ); removeList.add(6 ); removeList.add(3 ); removeList.add(4 ); removeList.add(5 ); removeList.add(6 ); removeList.add(4 ); System.out.println(removeList); removeList.remove(1 ); System.out.println(removeList); removeList.remove(new Integer (6 )); System.out.println(removeList); List<Integer> beRemovedList = new ArrayList <>(); beRemovedList.add(2 ); beRemovedList.add(3 ); beRemovedList.add(6 ); System.out.println(removeList); System.out.println(beRemovedList); boolean b = removeList.removeAll(beRemovedList); System.out.println(b); System.out.println(removeList); removeList.clear(); System.out.println(removeList); ArrayList<Integer> operatorList = new ArrayList <>(); operatorList.add(1 ); operatorList.add(2 ); operatorList.add(3 ); operatorList.add(2 ); operatorList.add(1 ); System.out.println(operatorList); operatorList.set(1 ,6 ); System.out.println(operatorList); Integer integer = operatorList.get(1 ); System.out.println(integer); Object clone = operatorList.clone(); System.out.println(clone); System.out.println(operatorList.size()); System.out.println(operatorList.isEmpty()); System.out.println(operatorList.indexOf(1 )); System.out.println(operatorList.lastIndexOf(1 )); System.out.println(operatorList.contains(3 )); System.out.println(operatorList.contains(4 )); List<Integer> iteratorList = new ArrayList <>(); iteratorList.add(1 ); iteratorList.add(2 ); iteratorList.add(3 ); iteratorList.add(4 ); iteratorList.add(5 ); iteratorList.add(6 ); Iterator<Integer> iterator = iteratorList.iterator(); while (iterator.hasNext()) { Integer next = iterator.next(); System.out.println(next); } for (int i = 0 ,size = iteratorList.size(); i < size; i++) { System.out.println(iteratorList.get(i)); } for (Integer i : iteratorList) { System.out.println(i); } } }
总结 ArrayList
集合是我们在工作中用到的最多的集合,我们必须熟练掌握其特性。
通过上面的源码分析可知,ArrayList
集合查找效率非常高。顺序添加元素至末尾效率也很高,但需要确保不扩容,否则进行数组拷贝很耗时。所以我们在创建ArrayList
对象时,如果可以预估到集合中元素个数,最好指定初始容量,以避免在插入时扩容带来的性能损耗。
本文Demo
实战代码见:传送门