ArrayList
今天我们来详细看一下ArrayList的源码.首先我们来看一下ArrayList的实现和继承
public class ArrayList<E>extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
面试常问问题!
为什么ArrayList查询比较快?
1.底层的数据结构
2.RandomAccess 快速访问 只是其标记作用
一、构造方法
- 用指定的大小来初始化内部数组
public ArrayList(int initialCapacity) // 阿里巴巴手册推荐使用 10
- 默认构造器,以默认大小来初始化内部数组
public ArrayList()
- 接收一个 Collection 对象,并将该集合的元素添加到 ArrayList 中
public ArrayList(Collection<? extends E> c)
二、常用方法
boolean add(E e) //将指定的元素追加到此列表的末尾。
void add(int index, E element) //在此列表中的指定位置插入指定的元素
E get(int index) //返回此列表中指定位置的元素。
E remove(int index) //删除该列表中指定位置的元素。
boolean remove(Object o) //从列表中删除指定元素的第一个出现(如果存在)
E set(int index, E element) //用指定的元素替换此列表中指定位置的元素。 //返回删除元素
List<E> subList(int fromIndex, int toIndex)
//返回此列表中指定的 fromIndex (包括)和 toIndex之间的独占视图。
具体的方法代码就不写了小伙伴们可以自己试一下,这里着重讲解一下subList这一个方法
2.1subList方法
List<Integer> list = new ArrayList<>(10);
list.add(100);
list.add(200);
list.add(300);
list.add(400);
List<Integer> subList = list.subList(0, 2);
subList.add(222);
System.out.println("list:"+list);// list:[100, 200, 222, 300, 400]
System.out.println("subList:"+subList);// subList:[100, 200, 222]
//如上代码所示当我再subList集合中添加一个222元素时,list集合中也会出现222
//说明subList与list集合公用一块内存
//对subList集合的add/delete/update都会映射源集合list上面
ArrayList<Integer> integers = new ArrayList<>(subList);
integers.add(222);
list.add(123);
System.out.println("integers"+integers); //integers:[100, 200, 222]
System.out.println("list"+list); //list:[100, 200, 300, 400, 123]
//如果一个List用了subList方法的话那么如果对源list集合做任何修改、删除、添加的操作
//都会触发ConcurrentModificationException异常
//解决方法-------------------------
subList = new ArrayList<>(subList);
//通过ArrayList的有参构造赋重新赋值subList
三、 add()源码
首先来一波语言概述:
ArrayList的构方法并没有给数组初始化空间,这个动态数组的初始化时间是在创建对后执行第一次add方法后才出现的。
首先判断集合元素的数组是否为空ensureCapacityInternal方法内的calculateCapacity方法,为空的话就是第一次初始化,返回DEFAULT_CAPACITY的值10。然后进入方法ensureExplicitCapacity进行扩容第一次扩容为0,扩容的新数组和返回过来的10做对比如果为负数就赋值给新数组然后用Arrats.copyOf方法将原数组元素elementData复制到扩容后的数组然后再赋值到原数组。接着赋值传入的数据给 elementData[size++]
当添加第11个元素时进行第二次扩容
第一步:创建数组(这里的创建数组不指定初始大小)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
// eg1:初始化ArrayList实例,则elementData={}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
第二步: add一个内容到数组中
// private int size; 维护集合的元素个数
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!! 扩容
elementData[size++] = e; //存值(size每次指向待插入的位置)size最开始是0。所以elementData[0] = e,然后size再+1
return true; //返回true
}
第三步:进入扩容方法ensureCapacityInternal
// eg1:第一次新增元素,所以size=0,则:minCapacity=size+1=1
private void ensureCapacityInternal(int minCapacity) {
// eg1:第一次新增元素,calculateCapacity方法返回值为DEFAULT_CAPACITY=10
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
第四步:进入比较大小方法calculateCapacity
// eg1:第一次新增元素,elementData={} minCapacity=1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 如果是第一次add的话才会进入if语句.
// eg1:满足if判断,DEFAULT_CAPACITY=10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
第五步:进入判断是否扩容方法ensureExplicitCapacity
// eg1:第一次新增元素,minCapacity=10
private void ensureExplicitCapacity(int minCapacity) {
// eg1: modCount++后,modCount=1
modCount++;
/** 如果所需的最小容量大于elementData数组的容量,则进行扩容操作 */
if (minCapacity - elementData.length > 0) {
// eg1:10-0=10,满足扩容需求
// eg1:minCapacity=10
grow(minCapacity);
}
}
第六步:进行扩容方法grow
// eg1:第一次新增元素,minCapacity=10,即:需要将elementData的0长度扩容为10长度。
private void grow(int minCapacity) {
/** 原有数组elementData的长度*/
int oldCapacity = elementData.length; // eg1:oldCapacity=0
/**
* A >> 1 等于 A/2
* eg: 3 >> 1 = 3/2 = 1
* 4 >> 1 = 4/2 = 2
* ------------------------
* A << 1 等于 A*2
* eg: 3 << 1 = 3*2 = 6
* 4 << 1 = 4*2 = 8
*
* 000100 >> 1 = 000010
* 000100 << 1 = 001000
*/
/** 新增oldCapacity的一半整数长度作为newCapacity的额外增长长度 */
int newCapacity = oldCapacity + (oldCapacity >> 1); // eg1:newCapacity=0+(0>>1)=0
/** 新的长度newCapacity依然无法满足需要的最小扩容量minCapacity,则新的扩容长度为minCapacity */
if (newCapacity - minCapacity < 0) {
// eg1:newCapacity=10
newCapacity = minCapacity;
}
/** 新的扩容长度newCapacity超出了最大的数组长度MAX_ARRAY_SIZE */
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
/** 扩展数组长度为newCapacity,并且将旧数组中的元素赋值到新的数组中 */
// eg1:newCapacity=10, 扩容elementData的length=10
elementData = Arrays.copyOf(elementData, newCapacity);
}
说了这么多还是不太理解怎么办、除了自己走一遍流程才能清楚明白。最好的方法就是看图片了。
四、 remove()源码
首先判断删除元素是否为null然后进入fastRemove方法删除元素获取 int numMoved = size - index - 1;的值如果大于0就 System.arraycopy操作,将index后面的元素向前位移一位。之后size的值置为null删除成功
如果使用remove()建议通过索引删除
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);
//如果删除的元素为最后 一个就不执行 System.arraycopy操作
elementData[--size] = null; // clear to let GC do its work
}
五、 get()源码
public E get(int index) {
rangeCheck(index);//检测index范围
return elementData(index);//直接返回元素索引
}
六、其他问题
为什么Arraylist通过循环删除不了第一位置的元素而能删除倒数第二个元素
1.首先我们来看一下集合元素的第一个不能删除的情况
ArrayList<Integer> list = new ArrayList<>(10);
list.add(100);
list.add(200);
list.add(300);
list.add(400);
for (Integer integer : list) {
if(integer.equals(100))
list.remove(integer);
}
System.out.println(list);
=====================当我们执行以上操作的时候报异常
**Exception in thread "main" java.util.ConcurrentModificationException**
2.删除倒数第二个元素
for (Integer integer : list) {
if(integer.equals(300))
list.remove(integer);
}
//[100, 200, 400]成功
集合快速失败
fail-fast 迭代器设计模式;modCount----expectedModeCount
最后一个不遍历了:hasNext中的cursor==size所以可以删除第二个
cursor值累加size减少条件满足没有执行后面的代码所以没有抛出异常,
1.循环方法
============================普通for循环
ArrayList<Integer> list = new ArrayList<>(10);
list.add(23);
list.add(12);
list.add(54);
list.add(13);
for (int i = 0; i < list.size(); i++) {
System.out.println("元素"+i+":"+list.get(i));
}
============================================================
ListIterator<Integer> integerListIterator = list.listIterator();
while (integerListIterator.hasNext()){
Integer next = integerListIterator.next();
System.out.println(next);
integerListIterator.remove();//的删除功能
integerListIterator.add(200);//listIterator的新增功能
}
===============================================================
Iterator<Object> iterator = list.iterator();//集合的每个元素
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
===============================================================
list.forEach((element)->{
System.out.println(element);
});
2. 删除集合中多个元素
===========================================正常for循环删除
for (int i = 0; i < list.size(); i++) {
if(Integer.valueOf(12).equals(list.get(i))){
list.remove(i);
i--;
}
}
===========================================removeIf删除
list.removeIf(s->Integer.valueOf(12).equals(s));
3. 排序
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
Collections.sort(list);