集合类List概念讲解源码分析

1:List介绍与底层以及扩容机制:

首先List 是集合类中的一个接口,继承了Collection接口,List 的实现类有ArrayList、LinkedList、Vector等。List集合中的元素是有序可重复的并且有下标的。ArrayList集合和Vector集合的底层使用Object类型是数组实现的。由于数组的长度是固定的所以当集合中的元素被装满时就需要扩容。ArrayList调用无参实例化后是空数组没有长度的,当调用增加方法时,会开辟10个长度,当ArrayList中的元素被装满时会以1.5倍的长度扩容,底层是以数组长度+数组长度位移>>1实现,所以是1.5倍,LinkedList底层node节点,记载了本身的值和上一个下一个的相邻元素的位置。linkedList 是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。List集合是有下标的可以使用for循环通过下标遍历,也可以使用 迭代器 Iterator或forEach等不通过下标的方式遍历

2:ArrayList linkedList Vector区别

ArrayList和LinkedList 和Vector都实现了 List接口,所以他们中的一部分操作和方法是相同的。不同在于底层的数据结构 和操作效率上。ArrayList 底层使用的是 数组式的顺序存储 ,特点是空间连续,读取操作效率高   一旦进行元素的插入或移除操作可能会降低效率。LinkedList 底层使用的是链式/链表存储  空间不连续 通过指针指向下一个元素,读取操作效率低 ,进行元素的插入或移除操作效率较高。当我们对集合进行 较多的读取操作时 建议是 ArrayList 当我们对集合进行 较多的插入或移除操作时 建议是 LinkedList。最后是线程方面,Vector 中的所有方法都是是带有 线程锁的 synchronized,这样使它在多线程环境中使用不会造成数据的混乱,更适合多线程的环境使用。

3:Arraylist源码:

List是个接口没有底层,所以直接讲解ArrayList底层

首先ArrayList继承于AbstractList抽象类,实现了,List,和序列化的接口,

ArrayList源码类中变量:

默认的数组长度   private static final int DEFAULT_CAPACITY = 10;

一个空数组    private static final Object[] EMPTY_ELEMENTDATA = {};

底层数组    transient Object[] elementData;

集合长度   private int size;

ArrayList集合底层是一个Object类型的数组elementData,所以list可以放入任意类型的元素,

3.1:无参构造:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//无参构造
public ArrayList() {
//可以看出实例化的时候调用无参构造,就是给null数组赋值给了一个{}空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}

ArrayList无参构造中默认实例化了一个空的object数组给elementData赋值,所以初始长度为0,在add增加的元素方法中会有一个方法判断ensureCapacityInternal,如果elementData是空数组的话那么会自动增加一个元素长度为10的数组,装入数据后赋值给elementData,默认长度是10 是由DEFAULT_CAPACITY这个变量规定,

3.2:有参构造:

//有参构造,创建 ArrayList时候指定长度
    public ArrayList(int initialCapacity) {
//长度大于0 创建指定的长度数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
//等于0创建空数组
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
//报异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

ArrayList有参构造中会传入一个集合长度initialCapacity,这是会判断如果initialCapacity大于0则创建这个长度的Object数组赋值给elementData,如果initialCapacity 等于0则用默认的空数组赋值给elementData,如果小于0则会抛出异常Illegal Capacity:

3.3:增加方法:

//增加方法
  public boolean add(E e) {

    //扩容方法:当数组长度不够了 ,增加数组的长度
    1:第一次扩容:0-10个长度
    2:第2次扩容:原数组的1.5扩容  
        ensureCapacityInternal(size + 1);  // Increments modCount!!


    //第一次增加将小明 放入数据0下标,放入之后,下标++方便下次增加使用
        elementData[size++] = e;
        return true;
    }

    /*
        扩容方法
    */
     private void ensureCapacityInternal(int minCapacity) {
        //第一次:minCapacity:1  赋值为10
        //判断 数组是不是空数组(刚创建的数据就是空数据  “{}”  )
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //将数组长度minCapacity :重新赋值 
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //第一次将10传递进去  调用 扩容方法
        ensureExplicitCapacity(minCapacity);
    }


    /*
        扩容方法
    */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
    //第一次:modCount=1
    //1-数组长度0 大于0走 grow进行扩容   如果不大于数组长度 就不用扩容
        if (minCapacity - elementData.length > 0)
    //调用成长方法,进行数组扩容,
            grow(minCapacity);
    }


    /*数组最大长度*/
     private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    //第一次调用数据扩容方法
    private void grow(int minCapacity) {

        //老数组容量=0(因为数组还没创建)
        int oldCapacity = elementData.length;

        // 计算 新数组创建容量 = 老容量+老容量>>1     “>>” 右移的意思是除 几次 2 
        //右移是除以几次2  位移计算效率高 新长度=老长度+老长度/2 就是扩容1.5倍的意思
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //新长度-最小长度  0-10
        //如果是第一次调用增加,数组没有长度需要赋值为10
         if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
 
         //如果新的数组长度 超过了  最大数组长度
        //(默认是int最大值-8,就规定为int最大值 大约21y)
        //也就是数据最大长度不可超过21y
        if (newCapacity - MAX_ARRAY_SIZE > 0)
        //数组长度,最大就是21y int最大值-8
            newCapacity = hugeCapacity(minCapacity);

        //调用Arrays数组中的copyof :此方法的意思是创建一个新的数据,
        //并可以将原数组数据复制到新数组中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }


3.4:查询方法

    /*查询方法 根据下标get*/
    public E get(int index) {
        //区间检查-看下标是否超过数组最大下标,
        rangeCheck(index);

        return elementData(index);
    }


    private void rangeCheck(int index) {
        //下标大于等于了数据长度,证明无此洗标,抛出下标越界异常
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    //直接根据下标返回数组中对应元素
    E elementData(int index) {
        return (E) elementData[index];
    }

ArrayList中的get方法,获取数组会传入下标,首先有个rangeCheck方法判断当前下标是否大于数组长度,如果大于报异常,如果小于则返回elementData【下标】

ArrayList中的add方法,会先判断是否是elementData是否是空数组,如果不是,则会elementData【size++】=值,如果数组长度不够用则会进行当前数组长度1.5倍扩容

ArrayList    底层代码有很多,列举了一些,其他的不一一列举

步骤,vector底层与ArrayList底层实现基本一致,只不过在所有的方法上都有synchronized修饰

4:LinkedList源码

LinkedList底层是基于双向链表结构设计的,有一个node节点  节点有三个属性,一个是prev上级节点,一个是next下级节点,另一个是node本身item,当增加元素的时候,要创建一个node。如果是第一次创建,则将元素赋值在node中的本身item属性,prev next 设置为null, 但是会将 node 标记为first,和last属性,证明是链表中第一个元素也是最后一个元素,然后第二次增加 的时候,将第二次元素设置为item 将 取出最后一个元素last 赋值为第二次元素中的上级信息prev 在将第2次元素 赋值到last属性。这个时候,链表中有两个元素,第二个元素是最后一个属性了,然后在增加元素的操作就跟第二次增加一样了,不停的创建node节点,增加一个元素就将元素设置为last就可以

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值