ArrayList类中的方法与实现原理
目录
- 一.数据结构
- 二.类标题
- 三 .字段
- 四.构造函数
- public ArrayList(int initialCapacity)
- public ArrayList()
- public ArrayList(Collection c)
- 五.其他方法
- public boolean add(Object o)
- public void add(int index,Object element)
- .public boolean addAll(Collection c)
- public boolean addAll(int index,Collection c)
- public void clear()
- public Object clone()
- .public boolean contains(Object elem)
- public boolean containsAll(Collection c)
- public boolean equals(Object o)
- public Object get(int index)
- public int hashCode()
- public int indexOf(Object elem)
- public boolean isEmpty()
- public Iterator iterator()
- public int lastindexOf(Object elem)
- public ListIterator listIterator()
- public ListIterator listIterator(final int index)
- public boolean remove(Object o)
- public Object remove(int index)
- public boolean removeAll(Collection c)
- public boolean retainAll(Collection c)
- public Object set(int index,Object element)
- public int size()
- public List subList(int fromIndex,int toIndex)
- public Object[] toArray()
- public Object[] toArray(Object []a)
- public String toString()
- public void trimToSize()
- 五.参考资料
一.数据结构
ArrayList的底层数据结构就是一个数组,数组元素的类型为Object类型,对ArrayList的所有操作底层都是就与数组的。
对ArrayList进行添加元素的操作的时候是分两个步骤进行的,即第一步先将Object[size]的位置上存放需要添加的元素;第二步将size的值加1。由于这个过程在多线程的环境下是不能保证具有原子性的,因此ArrayList在多线程的环境下是线程不安全的。
如果非要在多线程的环境下使用ArrayList,就需要保证它的线程安全性,通常有两种解决办法:第一,使用synchronized关键字;第二,可以用Collections类中的静态方法synchronizedList();对ArrayList进行调用。
二.类标题
ArrayList类的标题如下:
public class ArrayList extends AbstractList implements List,Cloneable,java.io.Serializable
这个标题说明ArrayList类是AbstractList类的子类,并且实现了三个接口:List、Cloneable和Serializable。如下图所示:
[Q1]为什么ArrayList继承了AbstractList还要实现List接口
Serializable接口源码:
package java.io; public interface Serializable { }
凡是实现Serializable接口(java.io)的类都可以进行序列化和反序列化操作
[Q2]:Serializable为什么要定义一个空接口来进行标识
[Q3]:如何理解: implements Cloneable表示该对象能被克隆,能使用Object.clone()方法。如果没有implements Cloneable的类调用Object.clone()方法就会抛出CloneNotSupportedException。
三.字段
private transient Object elementData[];
private int size;//数组中元素的个数
protected transient int modCount = 0; //继承自AbstractList
【注】:成员变量modCount记录着集合的修改次数,也就是每次add或者remove它的值都会加1
四.构造函数
1.public ArrayList(int initialCapacity);
作用:ArrayList类的构造方法,根据传入的initialCapacity值创建一个initialCapacity大小的数组。
源码如下:
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); } }
源码解析:
传入initialCapacity为0,创建空数组,否则,创建intialCapacity大小的数组elementData
2.public ArrayList();
作用:ArrayList类的构造方法,创建一个空的数组。在第一次添加元素时容量扩大到10
源码如下:
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
源码分析:创建一个空数组
3.public ArrayList(Collection c);
作用:使用指定Collection来构造ArrayList的构造函数
源码如下:
public ArrayList(Collection c) { elementData = c.toArray();//将传入的集合转换为一个数组,并将数组的应用赋给elementData if ((size = elementData.length) != 0) {//数组不为空 // defend against c.toArray (incorrectly) not returning Object[] if (elementData.getClass() != Object[].class)//数组不为Object[],将其复制成为Object elementData = Arrays.copyOf(elementData, size, Object[].class); } else {//数组为空的情况 // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
分析:
1.将传入的集合转换为数组
2.判断转换后的数组是否为空
若为空:创建一个空的elementData数组
若不为空,且转换后的数组类型不为Object[]类型,将其复制为符合Object[]数组类型的elementData数组
五.其他方法
1.public boolean add(Object o);
作用:将指定元素追加到列表的末尾。返回值是true
源码如下:
public boolean add(E e) { ensureCapacityInternal(size + 1); //判断是否需要扩容 elementData[size++] = e; //将元素插入到已有元素后一个位置 return true; } //将预计插入元素后所需最小容量传入EnsureCapacityInternal private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果调用的无参构造方法(创建空数组),minCapacity=10 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 如果传入的预计插入后的最小容量大于当前数组的容量,用grow进行扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } //对elementData[]进行扩容,每次增加1/2 elementData数组长度。 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); 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: // 调用 Arrays.copyOf 方法将 elementData 数组指向新的内存空间 newCapacity 的连续空间 // 并将 elementData 的数据复制到新的内存空间 elementData = Arrays.copyOf(elementData, newCapacity); }
源码分析:
ArrayList集合add()过程如下图所示:
[Q4]add(E e)方法为什么总是返回true?
不理解:[此方法总是返回true,是因为在Collection接口中对应的方法返回的是一个boolean值:如果调用对象所属类允许重复,或者被添加的元素尚不再调用对象中,就返回true,否则返回false.有些Collections接口的实现就不允许重复]
【注】Max_ARRAY_SIZE定义为private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE-8 其中,Integer.MAX_VALUE是Integer类中的一个int类型的常量MAX_VALUE,它代表int所能表示的最大值Ox7FFFFFFF
2.public void add(int index,Object element);
作用:将指定元素插入此列表的指定位置。将当前位置的元素(如果有)和任意后续元素向右移动。
源码如下:
//elementData数组在索引index处插入元素 public void add(int index, E element) { rangeCheckForAdd(index);//检查index索引是否越界 ensureCapacityInternal(size + 1); //扩容,源码在1.public boolean add(Object o)c处 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //java.lang.System /* @Function:复制数组,以插入元素,但是要将index之后的元素都往后移一位。然后就是插入元素,增加sized的值 @src:源数组 srcPos:源数组要复制的起始位置 dest:目的数组 destPos:目的数组放置的起始位置 length:复制的长度 */ public static native void arraycopy(Object src,int srcPos,Object dest, int destPosint length);
源码分析:
1.边界检查。检查传入的index索引是否越界。
2.容量判断。判断数组是否有足够空间接收元素,不够的话进行扩容操作。
3.右移。对索引后的元素进行右移一位操作。
4.插入元素。将新元素插入到数组索引处。
5.记录数组元素个数的size+1.
System.arraycopy(elementData, index, elementData, index + 1, size - index);
复制源数组elementData从index处开始size-index长度(复制index后面的元素)到elementData数组的index+1位置。图示如下:
[Q5]native关键字的使用
3.public boolean addAll(Collection c);
作用:将指定集合中的所有元素插入此列表末尾
源码如下:
public boolean addAll(Collection c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // 同4.public boolean add(Object o)分析,确保数组容量足够 System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }
源码分析:
1.将集合转换为数组。
2.确保加入新数组元素后,数组容量足够
3.将转换后的数组添加到已有数组末尾
4.记录数组元素个数的size+numNew
System.arraycopy(a, 0, elementData, size, numNew);
复制a数组从索引0-numNew的元素到elementData数组的size位置。源码及分析在2.public void add(int index,Object element)处;
4.public boolean addAll(int index,Collection c);
作用:从指定位置开始,将指定集合中的所有元素插入此列表。将当前位置的元素元素往后移。参数列表中两个参数,第一个参数,填写数组下标,第二个参数,直接填写要被插入的数组名即可。
源码如下:
public boolean addAll(int index, Collection c) { rangeCheckForAdd(index);//索引边界检查,源码在); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // 确保容量足够,源码在1.public boolean add(Object o); int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved);//源码在2.public void add(int index,Object element) System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }
源码分析:
1.索引边界检查
2.将要插入的集合转换为数组
3.确保加入新数组元素后,数组容量足够
4.计算已有数组在index后的元素个数
5.将已有elementData数组index后的numMoved位元素右移numNew位
6.将常茹为numNew位的新数组插入到index的位置
7.记录elementData数组元素个数的size+numNew
5.public void clear();
作用:从此列表中删除所有元素。
源码如下:
public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
源码分析:
成员变量modCount记录集合的修改次数。clear()方法原理即将elementData数组的所有元素赋空值。
6.public Object clone();
作用:返回此ArrayList实例的浅表副本。若要赋值给一个对象,需要强制转型。
源码如下:
public Object clone() { try { ArrayList v = (ArrayList) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } } //Arrays.copyOf().Arrays的copyOf()方法传回的数组是新的数组对象,改变传回数组中的元素值,不会影响原来的数组。copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值 public static byte[] copyOf(byte[] original, int newLength) {//original 需要复制的源数组 byte[] copy = new byte[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } //from java.lang.Object protected native Object clone() throws CloneNotSupportedException;
浅克隆:将新集合每个元素的引用指向原集合对应对象在栈空间的内存地址,所以,原集合修改或删除,克隆的集合随之改变;新集合修改添加会改变引用重新指向其他堆内存地址,删除就直接删除引用。
深克隆:将新集合的元素对象复制,在堆内存中重新开辟空间存一样的内容,一般要对集合中的对象重写clone(),在clone()中返回new的新对象,再add到新集合中,所以新旧集合操作互不影响。
源码分析:
ArrayList中的clone方法继承自Object类。ArrayList实现的clone()为浅克隆,即复制对象的引用。
1.利用Object类中的clone(),复制elementData数组的引用v
2.将已有数组elementData数组元素复制给v指向的elementData数组
[Q6]super.clone()进行了什么操作,v.elementData表示什么?
7.public boolean contains(Object elem);
作用:如果此列表中包含此元素,则返回true
源码如下:
public boolean contains(Object o) { return indexOf(o) >= 0; //indexOf(Object o)方法同15.public int indexOf(Object elem); }
源码分析:
查找elementData数组中是否包含o元素。
若包含,返回元素所在数组单元的索引值;
若不包含,返回-1.
8.public boolean containsAll(Collection c);
作用:方法用于检测 arraylist 是否包含指定集合中的所有元素。
源码如下:
public boolean containsAll(Collection c) { for (Object e : c) if (!contains(e))//源码在7.public boolean contains(Object elem); return false; return true; }
源码分析:
ArrayList类的containsAll(Collection c)继承自AbstractCollection类。
该方法用for-each遍历c集合,同时查看c集合中是否包含e元素。
若包含,则返回ture.否则返回false.
9.public boolean equals(Object o);
作用:这里调用的equals方法并不是Object类中的。ArrayList继承了AbstractList< E>,它的equals方法应该从这个抽象类中来的。发现该类确实重写了equals方法,就是使用迭代器遍历两个List每个元素是否相等。 那么ArrayList调用equals方法就是元素进行比较了。
源码如下:
//inheried from java.util.AbstractList public boolean equals(Object o) { if (o == this)//如果o为调用此方法的对象,则返回true return true; if (!(o instanceof List))//若对象o不是List对象,则返回false return false; //定义两个List迭代器用来遍历元素 ListIteratore1 = listIterator(); ListIterator e2 = ((List) o).listIterator(); while (e1.hasNext() && e2.hasNext()) {//当两个迭代器均有下一个元素 E o1 = e1.next(); Object o2 = e2.next(); if (!(o1==null ? o2==null : o1.equals(o2))) return false; } return !(e1.hasNext() || e2.hasNext()); }
源码分析:
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
若o1为空,返回o2 == null.则变为if(!(o2==null));此时,若o2不为null,则直接返回false.
若o1非空,返回o1.equals(o2).则变为if(!(o1.equals(o2)))【注】此处的equals()方法应为Object类中的equals()方法.若o1与o2不等,则返回false.
return !(e1.hasNext() || e2.hasNext());
若e1和e2都遍历完了,未返回false,则认定equals()返回true.
10.public Object get(int index);
作用:返回此列表中指定位置的元素
源码如下:
public E get(int index) { rangeCheck(index);//检查索引是否越界,源码在2.public void add(int index,Object element)处 return elementData(index); } E elementData(int index) { return (E) elementData[index]; }
源码分析:
1.检查索引是否越界
2.返回elementData数组指定索引index位置的元素。
11.public int hashCode();
作用:求ArrayList的哈希值,这个方法是ArrayList的抽象父类AbstractList中的方法
源码如下:
//inherited from java.util.AbstractList public int hashCode() { int hashCode = 1; for (E e : this) hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); return hashCode; }
源码分析:
在JAVA语言中,判断两个对象是否相等,一般有两种方法,一种是hashcode(),另一种是equals(),这两个方法在判断准确性和效率上有很大的区别。hashCode()方法和equal()方法的作用其实一样,在Java里都是用来对比两个对象是否相等一致,那么equal()既然已经能实现对比的功能了,为什么还要hashCode()呢?
因为重写的equal()里一般比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高,那么hashCode()既然效率这么高为什么还要equal()呢?
因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠。
12.public int indexOf(Object elem);
作用:返回此列表中元素第一次出现的位置下标,如果没有此元素则返回-1
源码如下:
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; }
源码分析:
ArrayList类中indexOf(Object elem)重写了AbstractList类中的方法。
1.判断传入元素是否为空
若为空,遍历数组元素,用“==”找出数组单元元素为空所在的索引
若不为空,遍历数组元素,用“equals()”找出数组中与传入元素内容相同的单元所在的索引
否则,返回-1。
13.public boolean isEmpty();
作用:判断此列表中是否为空,空则返回true,否则返回false
源码如下:
public boolean isEmpty() { return size == 0; }
源码分析:
如果记录数组元素个数的size==0,表示数组中没有元素,所以返回true.否则,返回false.
14.public Iterator iterator();
作用:返回当前集合的双向迭代器
每个实现Iterable接口的类必须提供一个iterator方法,返回一个Iterator对象,ArrayList也不例外
源码如下:
public Iteratoriterator() { return new Itr(); }
返回的是一个Itr类的对象,接下来我们来看它的源码
private class Itr implements Iterator{ int cursor = 0;//指向下一个要被迭代的元素 int lastRet = -1;//指向当前元素 int expectedModCount = modCount;//在迭代器初始化过程中会将modCount赋给迭代器的expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示通过其他方法修改了 ArrayList的结构 public boolean hasNext() { return cursor != size(); } public E next() {//将cursor指向下一个被迭代的元素,lastRet指向当前元素 checkForComodification();//检查是否有线程在修改数组结构 try { int i = cursor; E next = get(i); lastRet = i; cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet);//删除lastRet指向的数组单元 if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } //有两个线程(线程A,线程B),其中线程A负责遍历list、线程B修改list。 -线程A在遍历list过程的某个时候(此时expectedModCount = modCount=N),线程启动, 同时线程B增加一个元素,这是modCount的值发生改变(modCount + 1 = N + 1)。 线程A继续遍历执行next方法时, 通告checkForComodification方法发现expectedModCount = N , 而modCount = N + 1,两者不等 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
源码分析:
每个实现Iterable接口的类必须提供一个iterator方法,返回一个Iterator对象,ArrayList也不例外
cursor--;
expectedModCount = modCount;
如图所示,Cursor初始状态是指向5,即若调用ArrayList的迭代器使用next()遍历数组,在遍历途中使用ArrayList.remove(1),则会跳原本应该遍历的5,直接遍历到6.采用上述代码后,它在每一次删除之后都会将cursor(下一项)的位置设置为当前位置,也就是将cursor往前移动了一位,之后再将modCount赋值给expectedModCount使它们保持相等。
15.public int lastindexOf(Object elem);
作用:返回此列表中元素最后一次出现的位置下标,如果没有则返回-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; }
源码分析:
若查找数组中指定元素最后一次出现的位置下标,从后往前遍历elementData数组,找到该元素第一次出现位置的索引值。
16.public ListIterator listIterator();
作用:从0开始,按当前集合中现有的元素进行迭代(双向迭代)
源码如下:
//inherited from java.util.AbstractList public ListIteratorlistIterator() { return listIterator(0); } public ListIteratorlistIterator(final int index) { rangeCheckForAdd(index);//检查索引是否越界 return new ListItr(index);//返回ListIter对象 }
private class ListItr extends Itr implements ListIterator{ ListItr(int index) { cursor = index;//cursor表示当前元素索引 } public boolean hasPrevious() { return cursor != 0; } public E previous() {//返回当前元素的前一个元素 checkForComodification(); try { int i = cursor - 1; E previous = get(i); lastRet = cursor = i; return previous; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public int nextIndex() {//返回当前元素后一个元素的索引值 return cursor; } public int previousIndex() {//返回当前元素前一个元素的索引值 return cursor-1; } public void set(E e) {//修改当前对象的属性 if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.set(lastRet, e); expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } public void add(E e) {//向数组中添加对象 checkForComodification(); try { int i = cursor; AbstractList.this.add(i, e); lastRet = -1; cursor = i + 1; //分析类似17.public Iterator iterator(); expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } }
源码分析:
Iterator与ListIterator代码结构大同小异。其主要区别体现在以下:
(1)ListIterator有add()方法,可以向List中添加对象,而Iterator不能
(2)ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。
(3)ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
(4)都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。
17.public ListIterator listIterator(final int index);
作用:返回指定下标(包含该下标)后的值
源码如下:
public ListIteratorlistIterator(final int index) { checkForComodification();//判断是否有线程修改数组 rangeCheckForAdd(index);//判断索引是否越界 final int offset = this.offset; return new ListIterator() { int cursor = index; int lastRet = -1; int expectedModCount = ArrayList.this.modCount; public boolean hasNext() {//是否存在下一元素 return cursor != SubList.this.size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= SubList.this.size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (offset + i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[offset + (lastRet = i)]; } public boolean hasPrevious() { return cursor != 0; } @SuppressWarnings("unchecked") public E previous() { checkForComodification(); int i = cursor - 1; if (i < 0) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (offset + i >= elementData.length) throw new ConcurrentModificationException(); cursor = i; return (E) elementData[offset + (lastRet = i)]; } @SuppressWarnings("unchecked") public void forEachRemaining(Consumer consumer) { Objects.requireNonNull(consumer); final int size = SubList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (offset + i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[offset + (i++)]); } // update once at end of iteration to reduce heap write traffic lastRet = cursor = i; checkForComodification(); } public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { SubList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = ArrayList.this.modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.set(offset + lastRet, e); } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } public void add(E e) { checkForComodification(); try { int i = cursor; SubList.this.add(i, e); cursor = i + 1; lastRet = -1; expectedModCount = ArrayList.this.modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (expectedModCount != ArrayList.this.modCount) throw new ConcurrentModificationException(); } }; }
源码分析:
18.public boolean remove(Object o);
作用:移除指定元素
源码如下:
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; } //快速删除es数组的第i个元素 private void fastRemove(Object[] es, int i) { modCount++; final int newSize; if ((newSize = size - 1) > i) System.arraycopy(es, i + 1, es, i, newSize - i); es[size = newSize] = null; }
19.public Object remove(int index);
作用:删除指定位置的元素,后续元素往前补
源码如下:
public E remove(int index) { Objects.checkIndex(index, size); final Object[] es = elementData; @SuppressWarnings("unchecked") E oldValue = (E) es[index]; fastRemove(es, index); return oldValue; }
20.public boolean removeAll(Collection c);
作用:删除指定集合在此列表中的所有元素
源码如下:
//inherited from AbstractCollection public boolean removeAll(Collection c) { Objects.requireNonNull(c);//判断要移除的集合是否为空 return batchRemove(c, false); } /* @parameter:Collection c:目标集合 boolean complement:当前集合是否需要包含目标集合 false,则删除目标集合在当前集合中所存在的元素 true,则删除目标集合在当前集合中不纯在的元素 */ private boolean batchRemove(Collection c, boolean complement)// final Object[] elementData = this.elementData;//定义当前数组对象 int r = 0, w = 0; // r 在外层定义for循环所用下标,目的是为了判断有没有报错,如果报错了,r就!=size,如果没有报错r==size // w 进行操作的次数 boolean modified = false;//定义成功标识 try { for (; r < size; r++) if (c.contains(elementData[r]) == complement)//目标集合中元素存在或不存在 在当前集合中 elementData[w++] = elementData[r];//将不需要移除的元素放入elementData数组中 } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. if (r != size) {//表示报错了,与ArrayList的父类保持一致,当c.contains()这个方法抛出异常才会r != size System.arraycopy(elementData, r, elementData, w, size - r);//复制数组,从报错下标开始复制到 w下标(最后被修改的下标),复制长度是未成功循环的长度 w += size - r;//因为复制后数组长度就变了,所以需要求出目前数组的长度,w+ 复制的长度 } if (w != size) {// 表示原数组有删除了数据 // clear to let GC do its work for (int i = w; i < size; i++) //从w开始循环,循环到size,这些数据是要删除的数据,设为null elementData[i] = null; modCount += size - w;//修改操作次数计数 size-w表示删除的数量 size = w; //将长度设为新的长度 modified = true; } } return modified; }
源码分析:
1.判断要移除的集合是否为null,如果为空则抛出null异常
2.如果不为null,则调用batchRemove方法即可移除成功
2.1. 通过contains方法,将所有不需要移除的元素放入elementData数组中,然后标记最后一个放入元素的位置w
2.2. 将所有需要移除的元素设置为null
2.3. 将size的大小设置为w
2.4. 修改modCount字段(该阻断主要是为了防止多线程模式下安全问题)
2.5. 返回移除成功的标识
21.public boolean retainAll(Collection c);
作用:原数组和参数数组做交集,保留相同的部分。
源码如下:
//inherited from AbstractCollection public boolean retainAll(Collection c) { Objects.requireNonNull(c); return batchRemove(c, true);//源码在20.public boolean removeAll(Collection c)处 }
源码分析:
ArrayList类中的retainAll(Collection c)重写了AbstractCollection中的此方法
private boolean batchRemove(Collection
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)//目标集合中元素存在或不存在 在当前集合中
elementData[w++] = elementData[r];//将不需要移除的元素放入elementData数组中
removeAll()传入batchRemove中complement为false,表示将不存在的元素放入elementData数组
retainAll()传入batchRemove中complement为true,表示将存在的元素放入elementData数组中
22.public Object set(int index,Object element);
作用:用指定元素替换指定位置的元素。
源码如下:
public E set(int index, E element) { rangeCheck(index);//检查索引是否越界 E oldValue = elementData(index);//将需要修改位置的值赋给oldValue elementData[index] = element;//将element覆盖初值 return oldValue; }
23.public int size();
作用:返回此列表中的元素数
源码如下:
public int size() { return size; }
24.public List subList(int fromIndex,int toIndex);
作用:只返回从fromIndex到toIndex之间的数据元素。
源码如下:
public ListsubList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size);//索引边界检查 return new SubList(this, 0, fromIndex, toIndex); } static void subListRangeCheck(int fromIndex, int toIndex, int size) { if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); if (toIndex > size) throw new IndexOutOfBoundsException("toIndex = " + toIndex); if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); } //内部类SubList的构造函数: SubList(AbstractListparent, int offset, int fromIndex, int toIndex) { this.parent = parent; this.parentOffset = fromIndex; this.offset = offset + fromIndex; this.size = toIndex - fromIndex; this.modCount = ArrayList.this.modCount; }
源码分析:
25.public Object[] toArray();
作用:将当前集合中的所有元素转化成顶级Object类型对象数组返回
源码如下:
public Object[] toArray() { return Arrays.copyOf(elementData, size); }
源码分析:
ArrayList的数据结构本来就是一个数组,利用Object [] toArray()复制了一个对象
26.public Object[] toArray(Object []a);
作用:将集合中的元素依次存入参数数组中并返回。
源码如下:
publicT[] toArray(T[] a) { if (a.length < size)//如果传入数组的长度小于size,返回一个新的数组,大小为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);//所传入数组长度与size相等,则将elementData复制到传入数组中并返回传入的数组。 if (a.length > size)// 若传入数组长度大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。 a[size] = null; return a; }
27.public String toString();
作用:一个arraylist转换为字符串
源码如下:
//inherited from AbstractCollection public String toString() { Iteratorit = iterator();//集合本身调用迭代器方法(this.iterator),得到集合迭代器 if (! it.hasNext()) return "[]"; StringBuilder sb = new StringBuilder(); sb.append('['); for (;;) { E e = it.next(); sb.append(e == this ? "(this Collection)" : e); if (! it.hasNext()) return sb.append(']').toString(); sb.append(',').append(' '); } }
源码分析:
若集合没有元素->返回String类型"[]"
若集合有元素->先拼接'['->进入for死循环用append拼接e元素+','+' '->没有元素使用return跳出死循环并拼接']'
28.public void trimToSize();
作用:去除集合两端的null
源码如下:
public void trimToSize() { modCount++; if (size < elementData.length) {//如果时间大小小于缓冲区容量的长度,则进行数组复制。 elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
源码分析:底层数组的容量调整为当前列表保存的实际元素的大小的功能
六.参考资料
ArrayList中的Iterator详解
Java 集合中常见 checkForComodification()方法的作用? modCount和expectedModCount作用?
java面试(ArrayList去重;hashcode与equals)
Java 集合中关于Iterator 和ListIterator的详解
ArrayList之removeAll底层原理实现详解
ArrayList中remove、removeAll、retainAll方法源码解析
用大白话告诉你ArrayList的底层原理