ArrayList源码

xiaoxiao2021-02-28  96

简介:

1.ArrayList是基于数组的,一个动态“数组”,能够自动的扩容;

2.ArrayLIst不线程安全的,只能在单线程下使用,要想在多线程环境下使用, 可以使用集合操作类Colletcions.syncronized(List l)方法转化为一个线程安全的ArrayList 或者使用concurrent下面的CopyOnWriteArrayList类

3.ArrayList实现了序列化接口Serializable接口,能够序列化传输;实现了RandomAcess接口,支持快速随机访问,(实际上就是用过下标号进行访问);实现了Cloneable接口,能够被克隆

源码(JDK8)+部分注释

package java.util; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { //代码略 }

属性

/** *序列化版本号 */ private static final long serialVersionUID = 8683452581122892189L; /** * Default initial capacity. * 默认初始化的容量 10 */ private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 存放元素的数组,从这可以看出来底层就是数组 */ transient Object[] elementData; /** * The size of the ArrayList (the number of elements it contains). * 数组包含元素的个数 */ 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 * 数组的最大上限 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList的属性不多,重要的是elementData,声明这个属性的时候使用了transient关键字, transient用来表示一个域不是该对象序行化的一部分。

ArrayList在序列化的时候会调用writeObject,直接将size和element写入ObjectOutputStream;反序列化时调用readObject,从ObjectInputStream获取size和element,再恢复到elementData。

QA:为什么不直接用elementData来序列化??

原因在于elementData是一个缓存数组,他通常会预留一些空间,等容量不足时再扩容,那么有些空间可能并没有实际存在的数据,使用上述方式,实现序列化的时候,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间;

方法

构造方法

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); } } /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { 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) { 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; } }

1.用户自定义大小

带int类型的构造函数 如果传入参数,则代表指定ArrayList的初始数组长度,传入参数如果是大于等于0,则使用用户的参数初始化,如果用户传入的参数小于0,则抛出异常;

2.无参构造器

注意:初始化之后,此时,elementData的长度是1,size是0,那么默认值什么时候起作用呢? :) 在add的时候,elementData变成默认的长度;(参考源码可见)

3.带Collection对象的构造函数

将Colletion对象转换为数组,然后将数组的地址给elementData;如果elmentData长度为0,直接将 EMPTY_ELEMENTDATA赋给它;如果size的值大于0,则执行Arrays.copy方法,把collection对象的内容(可以理解为深拷贝)copy到elementData中

注意:elementData = c.toArray(); 这里执行的简单赋值时浅拷贝,所以要执行Arrays.copyOf 做深拷贝

get方法

public E get(int index) { //判断是否越界 rangeCheck(index); //返回 return elementData(index); } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //数组索引 E elementData(int index) { return (E) elementData[index]; }

从上面可以看出: 实际上就是从数组中取值,时间复杂度O(1)

add方法

public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } 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: elementData = Arrays.copyOf(elementData, newCapacity); }

如上:添加元素时,为了保证有足够的空间添加元素,如果是第一次添加,会将elementData的大小默认为10;如果不是第一次添加元素,就会跳入ensureExplicitCapacity方法,判断比较需要的空间与当前elementData的大小,如果不够,则扩容,即跳入grow()方法,增值原来的1.5倍;此时时间复杂度O(1)

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++; }

注意:时间复杂度O(n);

set方法

public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }

set会把原来位置上的数据返回;

remove方法

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; } 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; } 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 }

1.remove分两种:index和对象

根据index移除对象,如果是末尾的数据,直接将该位置的元素=null;如果是其他位置,需要将位置后面的元素全部往前移动一个位置,最后将最后一个位置=null;

根据对象,实现找到该对象对应的位置,然后根据位置去删除;(之后,同上);时间复杂度O(n)

indexOf()和lastIndexOf()

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; } 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; }

一个是从前往后找,一个是从后往前找;时间复杂度是O(n)。

subList()

public List<E> subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size); return new SubList(this, 0, fromIndex, toIndex); }

内部类SubList

trimToSize()

public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }

去掉多余的空间;

iterator()

public Iterator<E> iterator() { return new Itr(); }

内部类Ttr

Vecter

构造函数

public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; } public Vector(int initialCapacity) { this(initialCapacity, 0); } public Vector() { this(10); }

与ArrayList不同的是,Vector在初始化是就创建了数组;

grow()

private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }

在没有制定capacityIncrement的情况下,默认扩容方式是2倍;指定了扩容增长因子之后,每次增capacityIncrement大小的容量;

总结

ArrayList线程不安全的;Vector线程安全;写累了,歇会;

转载请注明原文地址: https://www.6miu.com/read-53003.html

最新回复(0)