写博客也有一段时间了,也上过两次推荐。但是总感觉自己的写博客和那牛人大神的博看还有还大差距,看别人的博客时发现别人的博客的技术水平很高,但是自己去写的时候却很难发现那些深奥的问题。我自己反思觉得主要时因为我开发经验不足,遇到的问题过少所导致的。这个问题不是一天两天能够解决的,我只能是多阅读牛人的博客,寻求真正的项目机会,在实战中积累知识、发现问题。
但这并不意味着在这段时间我就放弃写博客了。就读者而言仔细想来这或许也不是坏事,因为有很大的一部分读者跟我处于差不多的水平,学完Java没多久,想要进阶Java底层的深入研究。对这部分读者来说,看那些牛人大神的文章感觉很晦涩难懂,最终放弃。这时如果从我这种的文章入手就相对容易一点,毕竟我们有着差不多的理解深度。
废话不说,我们今天就从源码入手研究一下List容器吧。
- ArrayList
ArrayList的底层是使用数组进行实现的
可以看到我们向ArrayList中保存的数据都是存储在elementData数组中的,至于这个数组为什么使Object类型的,为什么不是我们指定的泛型,建议你阅读我的这篇博客泛型擦除。
我们知道数组是有容量的,所以我们在初始化ArrayList对象使必须使用指定数组的初始容量。
使用该构造方法就可以预先指定底层数组的初始大小。从代码可以看出,如果我们指定的数组初始大小等于0就会将数组初始化为EMPTY_ELEMENTDATA。如果我们使用不适用该构造方法指定数组的初始容量而是使用默认的空构造器,数组就会被初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。EMPTY_ELEMENTDATA、DEFAULTCAPACITY_EMPTY_ELEMENTDATA这两个常量本质上没什么区别(只是被初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的数组会在第一次添加数据时被扩充为10),就是两个空的数组。他们两个的定义如下图所示。
add()方法
我们都知道数组一旦确定了容量是不能改变大小的,那么我们向ArrayList容器中添加数据时如果数组的容量不足了内部是如何处理的呢?
可以看到在添加元素之前会先进行进行容量的判定,如果容量不足就使用grow方法进行扩充。实时上确定是否扩充扩充多少的的过程是一个较为复杂的过程我们就不将这些中间源码展开细说(没有太大的意思),我就简单说一下大致过程:
第一步,要确定所需要的最小容量(minCapacity),其实就时size + 1 和10选取最大值。上来先判断elementData是否等关于DEFAULTCAPACITY_EMPTY_ELEMENTDATA,如果等于则本次操作size + 1等于1,minCapacity取10;如果不等于minCapacity取size + 1。
第二步,判断容量最小值minCapacit与当前数组的容量作比较,如果当前容量大,则不需要扩充。如果当前容量小则调用grow方法将容量进行扩充。
下面我们来具体看grow方法是如何扩充数组容量的。
可以看到经过一系列的if确定新的数组容量之后,调用了Arrays.copyOf()方法返回了一个新的数组。copyOf方法我们都熟,就是new 一个新的指定容量的数组,然后将原数组的数据拷贝到新的数组(至于怎么拷贝的我不知道,因为最终它使用的不是Java实现的该过程)。但是不管怎么说这个过程要经历新对象的创建,数据的转移等过程,所付出的开销必然不小。
get()方法
看完了add操作之后就是get()方法了,其实知道了底层是用数组实现了之后,get方法的实现也就不难想象了,就是以下标为索引,获取数据。没什么可说的。
remove方法
从源码可以看出,也没什么稀奇的,就是依次遍历数组找到要删除的元素,然后调用fastRemove方法。
果然跟想象的一样,该方法就是把要删除的元素之后的元素向前移位,覆盖要删除的元素。只不过它用来移位的方法又是使用了非Java语言来写(上面的add方法的实现中最终就用了这个System.arraycopy方法)。
add(int index,E elem)方法(插入元素)
此方法方法就是在指定的地方插入元素。有了上面的经验,其实set方法的实现我们只要想象一下就可以知道。其实就是第一个add方法,和remove方法的结合而已。
。。。没啥可说的,先保证数组容量,然后数组移位,插入新元素。
能认真看完的都是有大毅力之人。如果感觉写的还可以的话,就来留下个赞把