Arrary的原理:ArrayList是由数组实现的,可以动态进行扩容。
用无参的构造函数创建list时,list的长度是0,也就是个空的数组,
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
但是当添加元素之后,也就是使用add()方法,由于现在数组长度为0,会触发扩容机制,这时候会扩容为10(这是最小的数组长度)
public boolean add(E e) {
++this.modCount;//记录这个list的元素修改次数
this.add(e, this.elementData, this.size);
return true;
}
ArrayList的扩容底层原理:
使用Array.copyOf时,在内部又创建了一个长度为newlength的数组,调用System.arraycopy()方法,将原来数组中的元素复制到了新的数组中。
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
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;
}
System.arraycopy()方法。该方法被标记了native,调用了系统的C/C++代码,在JDK中是看不到的,但在openJDK中可以看到其源码。该函数实际上最终调用了C语言的memmove()函数,因此它可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。
看博客看到一段关于size和modeCount的解释:
size和modCount的区别
可能看了源码有时候还分不清size和modCount的区别,那么这里就用例子来说明。
size是ArrayList的变量。modCount是ArrayList的父类AbstractList中的变量,默认值为0。
size记录了ArrayList中元素的数量,modCount记录的是关于元素的数目被修改的次数。modCount在ArrayList的普通操作里可能并没有看出多大用处,但是在涉及到fail-fast就主要是依靠它了相关解释
ArrayList的扩容机制:
扩容时,每次都是原来长度的1.5倍(最小的数组长度是10),如果在扩容后的数组长度还是不够,会直接用需要扩容到的长度来用
(例如,原来长度是10,需要增加10个元素。
源码里是这样处理的,newCapacity =10+10>>1 =》newCapacity = 10+10/2也就是10+5=15),但是如果长度还是不够的话,也就是newCapacity <minCapacity,会直接用需要的最小扩容长度(minCapacity),也就是20.
private int newCapacity(int minCapacity) {
int oldCapacity = this.elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//这里就是扩容长度,将原来的长度向右移位,也就是除以2。
if (newCapacity - minCapacity <= 0) {
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(10, minCapacity);//当是个空数组时,会给最小的10位为数组长度。
} else if (minCapacity < 0) {
throw new OutOfMemoryError();
} else {
return minCapacity;
}
} else {//这里-2147483639 是怕newCapacity超过int的最大值,如果没有超过的话,就直接用了newCapacity这个值来做数组长度(20),否则使用最大的int的值来做数组长度
return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
}
}
关于(oldCapacity >> 1)的解释
int len = 10;
int newLen = len >> 1;
System.out.println(newLen);
把10 转换成二进制为1010 向右移动一位 为 101 转为10进制等于5 加上原来的长度就扩容1.5倍
对于线程不安全的解释:
解决线程问题:
两种方法
//Synchronized对代码进行加锁,力度大,所以代码执行效率低下
List<String> list = Collections.synchronizedList(new ArrayList<String>());
//写时复制通过lock机制进行枷锁
/*CopyOnWrite容器即写时复制的容器。往一个容器添加元索的时候,不直接往当前容器Object[]添加,
而是先将当前容器Object[]进行Copy,复制出一个新的容器object[] newElements,
然后往新的容器object[] newElements 里添加元素,
添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);。
这样做的好处是可以CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
所以CopyOnWrite 容器也是一种读写分离的思想,读和写不同的容器*/
List<String> list = new CopyOnWriteArrayList();//这个很损耗性能,因为要开辟多一个内存空间
实现了Serializable接口,因此它支持序列化,能够通过序列化传输
RandomAccess接口,支持快速随机访问
https://blog.csdn.net/zhangxin961304090/article/details/46801225