Java集合的深入探讨

参考【Java集合后面的部分用的多吗?初级面试问的多吗? - Java3y的回答 - 知乎

Java中关于集合的内容是比较多的,主要的集合类型分为Set(无序)、List(有序)、Queue、Map。它们关系如下图,所有的Java集合类均是这【四个接口】的实现类。下面将依次进行介绍。
image

List

List在Java里边是一个接口,常见的实现类有ArrayList和LinkedList,在开发中用得最多的是ArrayList。

继承关系图

ArrayList的底层数据结构是数组,LinkedList底层数据结构是链表。

ArrayList

先来说说为什么Java中已经有了数组,还要增加 ArrayList。

我们知道,数组必须在创建(不是声明)时指定数组的容量,但很多时候我们并不知道要存放多少数据进去,这个容量设置大了就造成内存浪费,设置小了又容易导致溢出。而ArrayList不同,它会动态扩大数组的容量,这样就避免了原生数组的这两个问题。

首先,在创建ArrayList的时候,可以指定其初始容量大小,不指定的话默认容量为10。这可以从源码看到:(下面只复制部分源码进行展示)


    //默认容量大小
    private static final int DEFAULT_CAPACITY = 10;
	
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //这就是ArrayList底层用于存放数据的数组
    transient Object[] elementData;

    //指定初始化容量
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    //不指定容量时,用默认容量进行设置
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

transient关键字:实现了Serilizable接口的类,如果某个属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化到指定的目的地中

  1. 如何实现动态扩容?

当我们new ArrayList()的时候,默认会有一个空的Object数组,大小为0,源码如下:

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

当我们第一次add添加数据的时候,会给这个数组初始化一个大小,这个大小默认值为10
使用ArrayList在每一次add的时候,它都会先去计算这个数组够不够空间
如果空间是够的,那直接追加上去就好了。如果不够,那就得扩容

  1. 扩容,一次扩容多少?

在源码里边,有个grow方法,每一次扩原来的1.5倍。比如说,初始化的值是10,现在我第11个元素要进来了,发现这个数组的空间不够了,所以会扩到15(看下图第四块代码:newCapacity = oldCapacity + (oldCapacity >> 1),也就是新数组长度是old+old/2=1.5*old,即1.5倍旧数组长度)

空间扩完容之后,会调用arraycopy来对数组进行拷贝
image

为何在日常开发中用得最多的是ArrayList呢?

这是由底层的数据结构来决定的,在日常开发中,遍历的需求比增删要多,即便是增删也是往往在List的尾部添加就OK了。

像在尾部添加元素,ArrayList的时间复杂度也就O(1)

另外,ArrayList的增删底层调用的copyOf()被优化过,现代CPU对内存可以块操作,ArrayList的增删一点儿也不会比LinkedList慢

posted @ 2022-04-21 23:17  aJream  阅读(52)  评论(0编辑  收藏  举报