ArrayList
ArrayList是基于数组的实现,支持随机访问,可以动态扩容
初始化:
-
可以用带参的构造函数指定初始值,如果传入的参数是正数就创建指定容量的数组,如果是0就创建一个空数组,如果是负数,就抛出异常
-
默认大小是
10
添加元素:
-
添加元素时使用ensureCapacityInternal() 方法来保证容量足够,如果不够需要使用grow扩容,扩容的时候每扩容原数组容量的1.5倍
-
每次添加的时候都会修改
modCount
的值。modCount++ -
使用
Arrays.copyof()
把原数组的元素复制到新的数组中。 -
扩容1.5倍
-
如果新的容量小于需要的最小容量size+1,新容量就取最小容量size++(这种情况就是旧容量只有1,新容量1.5向下取模是1,小于添加元素后的最小容量2,这个时候新容量是size+1=2)
-
如果新的容量大于
MAX_ARRAY_SIZE
=Integer.MAX_VALUE-8
-
再判断需要的最小容量是否大于
MAX_ARRAY_SIZE
,如果是,新容量取值:Integer.MAX_VALUE
,否则,新的容量取值:MAX_ARRAY_SIZE
-
总结:
最小容量(a) 1.5倍扩容后的新容量(b) MAX_ARRAY_SIZE(c) Integer.MAX_VALUE(d)
if b<a 新容量=a;
if b>c if a>c 新容量=d
else 新容量=c
-
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); }
删除元素:
-
按照下标删除元素
-
如果下标超出size范围,抛出异常IndexOutOfBoundsException
-
modCount++;
-
判断要删除的元素是否在数组的结尾,如果在数组的结尾,就不需要调用
System.arraycopy()
,否则就需要调用System.arraycopy,通过numMoved=size-index-1
判断-
numMoved==0,是在数组结尾,elementData[--size] = null;
-
numMoved>0不是在结尾
System.arraycopy(elementData,index+1,elementData,index,numMoved);
-
-
返回删除的元素
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;
} -
-
根据元素进行删除
-
遍历element数组,把第一个用
equals
判断相等的元素删除(如果要删除的元素是null,使用==判断),确定待删除元素的index,然后调用私有方法fastremove()
跳过边界检查直接删除index位置上的元素,并且返回true
;如果元素不存在返回false
.
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; }
-
System.arraycopy()和Arrays.copyOf()的区别
-
System.arraycopy()
:native方法,使用System.arraycopy()进行数组的拷贝非常灵活, 可以选择拷贝的原数组的起点和长度, Arrays.copyOf()就是使用System.arraycopy()实现的arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
-
Arrays.copyof()
:Arrays.copyOf(original, length) 将源数组original中的元素拷贝到一个新数组中并返回这个新数组, 新数组的长度是length。copyOf(int[] original, int newLength)。
modCount的作用:
-
modeCount用来记录ArrayList结构性变化的次数,包括添加元素和删除元素,在进行序列化或迭代的时候,需要比较操作前后的modCount是否改变,如果改变了就会抛出
ConcurrentModificationException
,这种迭代称为Fail-fast模式
,常见的的使用fail-fast方式遍历的容器有HashMap
和ArrayList
等。 -
Fail-safe模式:这种遍历基于容器的一个克隆。因此,对容器内容的修改不影响遍历。常见的的使用fail-safe方式遍历的容器有
ConcerrentHashMap
和CopyOnWriteArrayList
等
Vector
-
使用
synchronized
同步,单步操作是线程安全的 -
可以通过构造函数传入初始容量和每次扩容的倍数,如果传入0和默认情况都是两倍扩容
-
Vector与ArrayList的比较
-
Vector 是同步的,开销就比 ArrayList 大,访问速度更慢,最好使用 ArrayList 而不是 Vector,因为同步操作完全可以由程序员自己来控制;
-
Vector 每次扩容请求其大小的 2 倍(也可以通过构造函数设置增长的容量),而 ArrayList 是 1.5 倍。
-
替代:可以使用 concurrent 并发包下的
CopyOnWriteArrayList
类替代Vector实现同步
-
CopyOnWriteArrayList:
通过读写分离实现了并发操作的安全性
-
读写分离
:写操作在一个复制的数组上进行,读操作还是在原始数组中进行,读写分离,互不影响。-
写操作需要加锁,防止并发写入时导致写入数据丢失。
-
写操作结束之后需要把原始数组指向新的复制数组。
-
-
适用场景:
适用于读多写少的场景 -
性能分析:
写操作每次都要拷贝数组,耗费内存;不能用于实时读的场景,因为写操作复制数组新增元素需要耗费时间,虽然能到达最终一致性,但是读写同时发生的时候读到的还是旧的数据。
LinkedList
-
基于双向链表的实现,使用Node表示链表节点信息。不支持随机访问,但是新增节点和删除节点的效率比ArrayList高
-
与 ArrayList 的比较
-
数组支持随机访问,但插入删除的代价很高,需要移动大量元素;
-
链表不支持随机访问,但插入删除只需要改变指针。
-