List接口底层实现类

List接口底层实现类

一、java集合类分类

java集合框架分为俩种,单列集合框架Collection,和双列集合框架Map

1.单列集合框架结构

Collection接口:单列集合,用来存储一个一个的对象

  •      |----List接口:存储序的、可重复的数据。  -->“动态”数组
         *          |----ArrayList、LinkedList、Vector
           ​
    
  •      |----Set接口:存储无序的、不可重复的数据   -->高中讲的“集合”
         *          |----HashSet、LinkedHashSet、TreeSet
    

本文将对List接口进行解析

对应图示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iT4sKTMA-1593178822089)(List接口底层实现类/1592968971414.png)]

实线为直接继承或实现,虚线为间接继承或实现

二、ArrayList

1.jdk1.7的情况

​ ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData

  • list.add(123);//elementData[0] = new Integer(123);

  • list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。

  • 默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。

  • 结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)

2.jdk1.8的情况

​ 由于现主流开发都是1.8版本的jdk,所有以下对ArrayList进行深入解读

3.继承情况

使用idea自带的可视化工具查看继承与实现情况,可以清楚的看到ArrayList间接的实现了Collection接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hG9mPXbo-1593178822091)(List接口底层实现类/1592969473(1)].jpg)

4.创建ArrayList

​ 接下来我们来看一下创建一个ArrayList容器会发生什么

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r5JCuIh4-1593178822095)(List接口底层实现类/1592969867(1)].jpg)

当我用debug创建一个arrayList时 返回的是一个空的,长度为0的数组

我们跟进去构造函数

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
transient Object[] elementData; //一个是Object[]对象,transient关键词是指该对象不需要被序列化
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//一个空的Object[]对象

与jdk7版本的区别是 jdk7创建后就创建一个长度为10的数组,但jdk8并没有,更加节省内存

5.添加数据

arrayList.add(123);

我们用debug跟进源码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8n6JCAgu-1593178822099)(List接口底层实现类/1592970609(1)].jpg)

因为存入123是基本数据类型,这里有一个自动装箱的过程(装换为Integer类型)

后面就是进入add方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0LathqMx-1593178822101)(List接口底层实现类/1592973839(1)].jpg)

size默认初始化为0,进入ensureCapacityInternal方法

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            //max方法取俩个参数最大的一个(这里DEFAULT_CAPACITY为10),将最大的值赋给minCapacity
        }

        ensureExplicitCapacity(minCapacity);
    }

进入ensureExplicitCapacity(minCapacity)方法

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//默认为0 

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)//判断最小容量减去原数组长度是否大于0
            grow(minCapacity);
    }   
进入grow(minCapacity)方法

 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//记录老容量长度 此时elementData长度为0
        int newCapacity = oldCapacity + (oldCapacity >> 1);//定义新容量值,老容量长度右移一位后加再上老容量长度,而右移一位是取数值的一半,这里就决定了ArrayList以后进行扩容都是对原数组扩容1.5倍
        if (newCapacity - minCapacity < 0)//0 - 10 小于0 所有进入 
            newCapacity = minCapacity;		//新容量长度就为10
        if (newCapacity - MAX_ARRAY_SIZE > 0) //判断此容量会不会超过最大长度(Integer.MAX_VALUE - 8)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//调用Arrays工具类,对数组进行扩容,原理为将旧数组数据复制到新数组
    }

三、LinkedList

1.继承情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e5CKjWHC-1593178822103)(List接口底层实现类/1592975059(1)].jpg)

可以看到不仅间接的实现了List接口,还间接的实现了队列Queue的接口

2.创建LinkedList

LinkedList linkedList = new LinkedList();

通过new的方式只是初始化LinkedList容器,底层双向链表结构还未创建。LinkedList Node类型的first和last属性,默认为null

3.添加数据

linkedList.add(123);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ZH307eo-1593178822105)(List接口底层实现类/1592975703(1)].jpg)

进入linkLast中

//在linkedList里面有first和last属性
transient Node<E> first;
transient Node<E> last;

void linkLast(E e) {
    final Node<E> l = last; //将原料的last对象赋值给l
    final Node<E> newNode = new Node<>(l, e, null);//Node为一个静态内部类 在下面:作用是初始化Node对象(链表对象,prev为头指针,next为尾指针,element为数据)
    last = newNode;//将有数据的newNode赋值给last
    if (l == null)//判断是否是第一个节点,如果是
        first = newNode;	//将NewNode直接赋值给尾节点
    else
        l.next = newNode;	//不是的话。将节点添加到l.next 相当于将链表连接起来
    size++;
    modCount++;
}

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

四、Vector

jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。

1.继承情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SBoqQmQv-1593178822107)(List接口底层实现类/1593002715(1)].png)

2.创建Vector

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WcHGuinx-1593178822109)(List接口底层实现类/1593002840(1)].jpg)

让我们跟进去构造函数

 public Vector() {
        this(10);
    }

再往下跟,调用一个参数的构造函数
public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
    
再往下跟,调用凉参数的构造函数
public Vector(int initialCapacity, int capacityIncrement) {
        super();		//显示调用父构造函数
        if (initialCapacity < 0)	//为0就报错,参数传进来为10
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity]; //创建并初始化了对象数组
        this.capacityIncrement = capacityIncrement;
    }
    
上面俩个属性定义如下:
protected Object[] elementData;
protected int elementCount;

此版本为jdk8,jdk7的Vector也是创建一个Vector容器就默认初始化长度为0,与jdk7的ArrayList一样,但是人家开发者对ArrayList进行了改进,使用懒加载,可能Vector要被放弃了吧!!

接下来讲为什么会放弃Vector

3.添加数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6SEkhBSs-1593178822111)(List接口底层实现类/1593003620(1)].jpg)

接下来跟进源码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfewpFDt-1593178822113)(List接口底层实现类/1593003749(1)].jpg)

elementCount默认为0。

可以看到有一个ensureCapacityHelper方法,此方法是校验长度是否足够,并去扩容的方法,如下:

private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);//扩容方法
}

但此处因为默认初始化长度为10,所以这里不会有扩容的需求。

稍等!Vector不是挺好的嘛,为什么要放弃他??因为…

public synchronized boolean add(E e)
public synchronized E remove(int index) 
public synchronized boolean containsAll(Collection<?> c)
public synchronized boolean addAll(int index, Collection<? extends E> c)

因为他的方法上全加了synchronized,加上这个字段他就是一个线程安全的方法,而他的全部方法都是线程安全方法,又众所周知,线程安全效率大大减低,所以连开发jdk人员都懒得救他,毕竟,出现了更好的人,老情人全忘掉了~

五、总结

上述介绍了List接口的全部实现类,如果数据不需要线程安全,则不要去考虑使用Vector,而因为ArrayList和LinkedList底层实现原理不同,他们使用的地方也有所不同,ArrayList底层使用的是数组实现,因此在需要频繁的插入和删除数据时效率会大大减低,而数组有一个好处,就是有下标,下标最直接好的好处就是查询速度非常的快,而LinkedList底层使用的是双向链表实现,所以在频繁的插入和删除数据时,有超高性能的表现,但是在查询的速度远远比不上ArrayList。

记重点:ArrayList数组实现,每次扩容1.5倍,使用懒加载,首次扩容长度为10

上述介绍了List接口的全部实现类,如果数据不需要线程安全,则不要去考虑使用Vector,而因为ArrayList和LinkedList底层实现原理不同,他们使用的地方也有所不同,ArrayList底层使用的是数组实现,因此在需要频繁的插入和删除数据时效率会大大减低,而数组有一个好处,就是有下标,下标最直接好的好处就是查询速度非常的快,而LinkedList底层使用的是双向链表实现,所以在频繁的插入和删除数据时,有超高性能的表现,但是在查询的速度远远比不上ArrayList。

记重点:ArrayList数组实现,每次扩容1.5倍,使用懒加载,首次扩容长度为10

​ LinkedList双向链表实现

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值