ArrayList学习[常用方法|源码]

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

说了这么多还是不太理解怎么办、除了自己走一遍流程才能清楚明白。最好的方法就是看图片了。

image-20210822162739055

四、 remove()源码

首先判断删除元素是否为null然后进入fastRemove方法删除元素获取  int numMoved = size - index - 1;的值如果大于0System.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);

猜你喜欢

转载自blog.csdn.net/weixin_45948874/article/details/119855161