1.ArrayList变量声明
/**
* Default initial capacity. 默认长度
*/
private static final int DEFAULT_CAPACITY = 10;
/**
*
* 默认的空数组对象
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
*
* 声明一个object数组对象
*/
transient Object[] elementData;
/**
* The size of the ArrayList (the number of elements it contains).
* 数组长度
*/
private int size;
2. 测试代码
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList();
for (int i = 0; i < 10; i++) {
arrayList.add("test" + i);
}
arrayList.add("test11");
}
3. 源码分析(添加及删除元素)
//数组添加方法
public boolean add(E e) {
//判断集合长度
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//如果第一次进来 默认返回默认的数组长度10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//这里判断集合是否需要扩容
//第一次初始化add元素 集合的size为10 所以添加10个元素以内都不会触发扩容操作
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
当添加第11个元素的时候 会触发扩容操作 扩容操作默认为原数组长度的1.5倍
当集合长度过大,大于 Integer.MAX_VALUE - 8 以后会触发hugeCapacity()方法,然后执行Arrays.copyOf复制扩容数组。
//ArrayList删除操作 遍历集合 获取删除元素的索引 调用fastRemove()方法进行删除
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;
}
// 这里主要是根据要删除的索引位置 获取删除索引后的元素集合,判断然后复制数组
// 最后将最后的位置的空位置设置为null 可以让gc回收 避免内存泄漏
// src:要复制的数组(源数组)
// srcPos:复制源数组的起始位置
// dest:目标数组
// destPos:目标数组的下标位置
// length:要复制的长度
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
}
4. 关于modCount
/*在新增和删除里都有modCount变量 modCount翻译为修改次数
这里的修改解释为修改元素的个数 即在遍历List等集合的时候调用add remove等方法使
元素个数发生改变*/
arrayList.forEach( item -> {
arrayList.add("test11");
System.out.println(item);
});
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
在一个迭代器初始的时候会赋予它调用这个迭代器的对象的mCount,如何在迭代器遍历的过程中,
一旦发现这个对象的mcount和迭代器中存储的mcount不一样那就抛异常。
5.Fail-Fast 机制
我们知道 java.util.ArrayList不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。这一策略在源码中的实现是通过 modCount 域,modCount 顾名思义就是修改次数,对HashMap 内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示已经有其他线程修改了 List集合。
1.所以在这里和大家建议,当大家遍历那些非线程安全的数据结构时,尽量使用迭代器。
2.如果必须在遍历时删除元素,建议使用迭代器的remove方法进行删除。
3.使用CopyOnWriteArrayList代替了ArrayList,就不会发生fail-fast异常。
Tips:在遍历删除的时候删除倒数第二个元素的时候不会发生fast-fail
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList();
arrayList.add("a");
arrayList.add("b");
arrayList.add("c");
arrayList.add("d");
for (String s : arrayList) {
if(Objects.equals("c",s)){
arrayList.remove("c");
}
}
for (String s : arrayList) {
if(Objects.equals("d",s)){
arrayList.remove("d");
}
}
System.out.println(arrayList);
}
6.arrayList和vector的区别
1.ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的扩 容需要将已经有数组的数据通过System.arraycopy()方法复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
2.Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
3.LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
4.vector是线程(Thread)同步(Synchronized)的,所以它也是线程安全的,而Arraylist是线程不安全的。如果不考虑到线程的安全因素,一般用Arraylist效率比较高。
5.vector扩容为数组长度的100%而arraylist的扩容是数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势。
笼统来说:
LinkedList:增删改快
ArrayList:查询快(有索引的存在) 线程不安全 扩容为原长度的1.5倍
Vector: 线程安全 查询比ArrayList慢 扩容为原长度的2倍