博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(周期计划-7)常用集合的源码分析:ArrayList
阅读量:6494 次
发布时间:2019-06-24

本文共 2813 字,大约阅读时间需要 9 分钟。

写在前面

最近因为拥抱变换,所以开始无奈的面试之路。因为在集合的源码分析上,出了些问题,所以这段时间,好好重新理一理常用的集合源码。(版本基于JDK1.7)

感兴趣的看官,可以看看我的其他文章:


ArrayList

毫无疑问,提到常用集合。ArrayList势必是第一个被搬出来的,因此我们就先拿它开刀了。

add(E e)

1、初始化

ArrayList的初始化,只有在第一次add的时候进行new数据,数组默认容量是10。

private static final int DEFAULT_CAPACITY = 10;

2、扩容

每次add,第一步先初始化,初始化过后,开始判断是否需要扩容。

扩容的判断,需要借助内部的一个size变量,这个变量用于统计当前数组的真实容量。也就是说我们的每一次add,size都会++。因此在我们进行扩容判断的时候,就是通过这个size和数组的当前最大容量进行比较。
如果if (minCapacity - elementData.length > 0)其中minCapacity==size+1。满足这个条件说明需要扩容。
扩容的过程比较直接,直接上代码:

private void grow(int minCapacity) {        int oldCapacity = elementData.length;        //扩容1.5倍        int newCapacity = oldCapacity + (oldCapacity >> 1);        //越界判断        if (newCapacity - minCapacity < 0)            newCapacity = minCapacity;        if (newCapacity - MAX_ARRAY_SIZE > 0)            newCapacity = hugeCapacity(minCapacity);        //拷贝数组        elementData = Arrays.copyOf(elementData, newCapacity);    }

3、赋值

赋值的过程,就没什么好说的了:

elementData[size++] = e;

OK,add方法的过程就先看到此。非常简单的过程。1、第一次add,初始化一个容量10的数组。2、每次add()需要判断是否需要扩容。3、扩容过程完毕,把数据查到数组中。


add(int index, E element)

接下来,让我们看一个往明确的下标中add的方法。

1、第一步

刚开始的过程没什么好说的,就是比add(E e)的过程,多一步先判断index是否越界;然后依旧是初始化,和扩容的过程。

//判断是否越界rangeCheckForAdd(index);//初始化以及扩容ensureCapacityInternal(size + 1);

2、移动数组

既然我们是往某个下标下插值,那么插入之前,我们就要给要插入的index空出位置来,也就是数组整体移动位置。也就是下边这行代码。

System.arraycopy(elementData, index, elementData, index + 1,                         size - index);

通过这个操作我们能看到一个问题,那就是如果我们连续进行相同的add(int index,E e)操作,那么很明显不会出现覆盖。所以,如果我们想要有覆盖的操作,需要调用set系列方法(后续会对此进行分析)。

这里必须得提一点,这里的操作因为需要移动数组,因此对于ArrayList来说,这是一个效率比较低的操作。

也印证了我们老生常谈的话题:数组适合随机读取;链表适合增加与删除。

3、赋值

没啥好说的,同上:

elementData[size++] = e;

get(int index)

关于get操作,其实没啥好特别留意的,很普通的操作。因为直接返回对应数组的index下标即可呐。

public E get(int index) {        //判断是否越界        rangeCheck(index);        return elementData(index);}

set(int index, E element)

其实覆盖操作,也没什么难,就是单纯把index那个数据覆盖了,仅此而已。

public E set(int index, E element) {        //判断越界问题        rangeCheck(index);        //覆盖数据        E oldValue = elementData(index);        elementData[index] = element;        return oldValue;    }

remove(int index)

remove的操作也没有特别需要注意的地方。基本上贴上代码,就能很清楚的明白:

public E remove(int index) {        rangeCheck(index);        modCount++;        E oldValue = elementData(index);        int numMoved = size - index - 1;        if (numMoved > 0)            System.arraycopy(elementData, index+1, elementData, index,                             numMoved);        elementData[--size] = null;        return oldValue;    }

基本上到这一步关于ArrayList的正常使用的分析就告一段落。当然,我们提到ArrayList,难免会扯到Vector身上。虽然它的身份略显尴尬,充其量就是一个跑龙套的角色。

而且它内部的保证线程安全的操作,仅仅是使用了synchronized关键字而已。因为就不增加篇幅去梳理它了。


尾声

结束ArrayList的分析,我们可以发现,ArrayList的实现,相对比较的简单。需要留意的点比较的少,大概就剩下了如果此次add,超出了现有数组容量,进行1.5倍扩容。

本菜开源的一个自己写的Demo,这个项目拆解并组合了很多业务。目的在于遇到类似业务,可以快速的ctrl+c/v。希望能给Androider们有所帮助,水平有限,见谅见谅…

转载地址:http://cruyo.baihongyu.com/

你可能感兴趣的文章
Objective-C中的isa、class、SEL、IMP
查看>>
head命令
查看>>
对高可用性的exchange2010的 Array配置
查看>>
操作系统中常用的进程调度算法
查看>>
puppet 使用
查看>>
一次网站负载排查记录
查看>>
Mina使用IoHandler实现业务处理
查看>>
The Competition
查看>>
LVM
查看>>
varnish 性能调优
查看>>
高可用网站的软件质量保证
查看>>
Libpcap tutorial-02
查看>>
java servlet简介-01
查看>>
中文乱码问题的处理
查看>>
Windows10 远程桌面连接失败,报CredSSP加密oracle修正错误解决办法
查看>>
egit在pull的时候出错
查看>>
Zabbix 中使用 Percona Monitoring Plugins 监控 MySQL
查看>>
我的友情链接
查看>>
5.Struts2-Struts标签
查看>>
各种技术综合总结(一)
查看>>