Java核心技术-集合-Collection接口-List-ArrayList

ArrayList

一、概述

ArrayList是平时开发中最常用的集合,底层是数组,相当于动态数组。与java中的数组相比,它的长度能动态增长

1、ArrayList的特点

  • 它是基于数组的List实现类
  • 它的元素可以有null
  • 它进出是有序的
  • 不同步,线程不安全,效率高
  • 查询快,增删慢
  • 相较与底层是链表的LinkedList集合,不需要额外的空间去维护底层链表结构占用空间更小
  • 可以动态的调整容量

2、所有方法

方法名参数作用
add任何对象添加元素到集合中
size获取当前集合中元素的数量
getint 角标获取传入角标下的元素
addAllColletion集合将另一个集合合并到当前集合中
addAllindex插入位置的角标,Colletion,集合将另一个集合从指定位置开始插入
contains元素对象判断传入的元素是否在当前集合中存在
indexOf元素对象根据传入的对象返回对象角标
remove元素对象根据传入的对象判断是否存在,存在就删除返回true否则返回false
remove角标根据传入的角标判断是否存在,存在就删除并将删除的元素返回
removeAllCoolletion集合根据传入的集合批量删除
removeIfSteam流对象按条件删除
clear清空集合

3、继承体系

在这里插入图片描述

ArrayList继承了AbstractList,实现了Cloneble,RandomAccess,Serializable

  • 实现List,提供了基础的添加,删除,遍历等操作
  • 实现了RandomAccess,提供了随机访问的操作
  • 实现了Cloneble,可以被克隆
  • 实现了Serializable,可以被序列化

二、源码分析

在这里插入图片描述

1、成员变量


	//序列号
 private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 数组初始容量为10
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空对象数组,为了减少空数组的产生,共享空数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
	 * 默认空对象数组,确保空参构造器时初始化的容量等于10,
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 底层的数组对象
     */
    transient Object[] elementData; 

    /**
     * 数组元素个数,默认为0
     */
    private int size;

2、构造方法

	//根据指定的传入的大小创建集合(底层数组)
 public ArrayList( int initialCapacity){
 	  //如果大于0就创建initialCapacity大小的数组
      if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
        //否则默认用空数组
      } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
      } else {
        throw new IllegalArgumentException("Illegal Capacity: " +
            initialCapacity);
      }
    }

	//默认空参构造器,初始化为空数组,只有当传入一条或一条以上的的元素才会扩容为10,否则默认为空
    public ArrayList() {
      this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

	//构造一个包含指定元素的列表
    public ArrayList(Collection < ? extends E > c){
      //将传入的集合转换为数组
      Object[] a = c.toArray();
      //如果传入的集合长度不等于0
      if ((size = a.length) != 0) {
      	//如果是ArrayList,直接赋值
        if (c.getClass() == ArrayList.class) {
          elementData = a;
        } else {
        	//转换成数组后再赋值
          elementData = Arrays.copyOf(a, size, Object[].class);
        }
        //如果长度等于0,赋值空对象
      } else {
        elementData = EMPTY_ELEMENTDATA;
      }
    }

当以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加操作时,才能真正的分配容量。即向数组添加第一个元素时,数组扩容为10

3、内部类

(1)private class Itr implements Iterator<E>  
(2)private class ListItr extends Itr implements ListIterator<E>  
(3)private class SubList extends AbstractList<E> implements RandomAccess  
(4)static final class ArrayListSpliterator<E> implements Spliterator<E>  

ArrayList有四个实现类:
Itr是实现了Iterator接口,同时重写了里面的hasNext(), next(), remove() 等方法;
ListItr是实现了ListIterator接口,同时重写了hasPrevious(), nextIndex(), previousIndex(), previous(), set(E e), add(E e);

4、核心方法

4.1、4个添加元素相关的方法

== 增和删是ArrayList最重要的部分==

  //添加一个元素到list的末尾
     public boolean add(E e) {
     	//判断是否需要扩容
        ensureCapacityInternal(size + 1);
        //已有元素+1,并把元素添加在正确角标下(注意并不是添加在元素+1角标下,因为++在后)
        elementData[size++] = e;
        return true;
    }
  	/**
  	*参数: 添加的元素,当前数组,当前元素个数长度
  	*重载的新增方法,调用add方法时,默认走这个方法
  	*/
    private void add(E e, Object[] elementData, int s) {
    	//首先判断当前元素数量是否等于当前数组的长度,如果是进行扩容
        if (s == elementData.length)
            elementData = grow();
        //不等于直接添加元素到数组中    
        elementData[s] = e;
        //加一操作,记录当前元素数量
        size = s + 1;
    }
    
    //在指定位置添加一个元素
    public void add(int index, E element) {
    	//判断角标是否合法
        rangeCheckForAdd(index);
		//判断是否需要扩容
        ensureCapacityInternal(size + 1); 
        //将传入的角标之后所有的数据向后移动一格
        System.arraycopy(elementData, index, elementData, index + 1,size - index);
        //在指定角标中添加元素
        elementData[index] = element;
        //元素长度+1
        size++;
    }
     public boolean addAll(Collection<? extends E> c) {
        //将传入的集合转为数组并赋值到a
        Object[] a = c.toArray();
        //获取当前传入集合的长度
        int numNew = a.length;
        //判断是否需要扩容
        ensureCapacityInternal(size + numNew);
        //将传入的集合添加到原集合的后面
        System.arraycopy(a, 0, elementData, size, numNew);
        //原集合长度+传入过来的集合长度
        size += numNew;
        return numNew != 0;
    }
   public boolean addAll(int index, Collection<? extends E> c) {
   		//判断角标是否合理(如果传入角标大于当前当前数据量并小于0不合理)
        rangeCheckForAdd(index);
		//将传入的集合转为数组并赋值到a
        Object[] a = c.toArray();
          //获取当前传入集合的长度
        int numNew = a.length;
           //判断是否需要扩容
        ensureCapacityInternal(size + numNew);  
        
        int numMoved = size - index;
        //判断是否需要后移原数组(逻辑如果传入的角标是刚好是当前集合的长度不需要后移)
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,numMoved);
        //把新的集合添加到指定位置
        System.arraycopy(a, 0, elementData, index, numNew);
         //原集合长度+传入过来的集合长度
        size += numNew;
        return numNew != 0;
    }
4.2、扩容相关方法

对集合进行添加元素时都会判断是否进行扩容,调用grow方法

	//确保内部容量够用
   private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    //判断是否需要扩容
   private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 判断是否需要扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    //计算容量,判断原数组是否是空数组
   private static int calculateCapacity(Object[] elementData, int minCapacity) {
   		//如果是空返回默认扩容长度或传入数
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    //扩容核心方法
     private void grow(int minCapacity) {
     	//获取原数组长度
        int oldCapacity = elementData.length;
        //扩充1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果说传入的数大于扩充1.5倍的值
        if (newCapacity - minCapacity < 0)
        	//赋值成传入数
            newCapacity = minCapacity;
        //如果需要扩容的数大于集合最大数 判断是给intger最大数还是集合最大数    
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
            //扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

4.3、大数据量插入

当大数据量插入时可以尝试两种方式:
1、使用ArrayList(int initialCapacity)这个有参构造器,在创建时就声明一个较大的大小,这样解决了频繁拷贝问题,但是需要我们提前预知数据量的大小,而且也会一直占用内存
2、使用ensureCapacity(int minCapacity)方法,其内部也是调用ensureExplicitCapacity(int minCapacity)进行扩容。

4.4、remove()方法

    public E remove(int index) {
    //检查index的合理性
        rangeCheck(index);
        modCount++;
        //通过传入的角标找到对应的元素
        E oldValue = elementData(index);
        //开始计算是否需要移动和移动的位置,
        int numMoved = size - index - 1;
        if (numMoved > 0)
        //将需要删除的元素后面那些元素整体向前移一位(这时候最后一位元素还在老位置)
      System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。
        elementData[--size] = null; 
        return oldValue;
    }

4.5、set()方法

4.6、get()方法

4.7、indexOf()方法

4.8、contains()方法

4.9、toArray()方法

5、分析System.arraycopy()和 Arrays.copyOf()方法

5.1、分析System.arraycopy()方法

System.arraycopy():将指定数组中的数组从指定位置开始复制到目标数据的指定位置

// src:源对象
// srcPos:源对象对象的起始位置
// dest:目标对象
// destPost:目标对象的起始位置
// length:从起始位置往后复制的长度。
// 这段的大概意思就是解释这个方法的用法,复制src到dest,复制的位置是从src的srcPost开始,到srcPost+length-1的位置结束,复制到destPost上,从destPost开始到destPost+length-1的位置上
public static void arraycopy(Object src, int srcPos, Object dest, int destPos,
             int length)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值