Java深究之Vector、ArrayList、LinkedList的区别

        在java开发中除了上文经常用的对字符串的操作外,还有使用居多的当属集合了。在基础的java学习时,相信很多同学都学习了List、Set、Map这些,他们之间的区别和基本的使用方法也是很了解了,本文是详细的去分析List中Vector、ArrayList、LinkedList之间的区别和底层实现原理以及使用场景

首先说下这三者的区别:

1.基本区别:三个类都实现了List接口,都是有序集合,数据是允许重复的;ArrayList 和Vector都是基于数组实现存储的,集合中的元素的位置都是有顺序即连续的;LinkedList是基于双向链表实现存储的集合中的元素的位置是不连续的

2.性能区别:Vector和ArrayList底层实现原理一致,但是Vector是线程安全的,因此性能比ArrayList差很多;LinkedList相比于集合Vector和ArrayList在插入,修改,删除等操作上速度较快,但是随机访问的性能较差

3.安全区别:Vector是使用了synchronized同步锁是线程安全的,ArrayList和LinkedList都是线程不安全的

4.原理区别:ArrayList与Vector都有初始的容量大小,当存储的元素的个数超过了容量时,就需要增加存储空间,Vector默认增长为原来两倍,而ArrayList的增长为原来的1.5倍;ArrayList与Vector都可以设置初始空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法

然后详细分析每一个的原理:

1.Vector

b205a631d7581de379853c361ea68722266.jpg

44b6026f9612455ec507803dd4325a5e63e.jpg

从源码可以看出Vector的初始默认空间大小为10,底层使用protected Object[] elementData;存储数据,使用protected int elementCount;存储元素数量,Vector的方法都被synchronized关键字修饰,因此是线程安全的。

接下来我们从源码上看下Vector的添加元素时以及初始容量不足时的扩容机制:

b3213733195ed4bdbaa41c38201b75f59c2.jpg

576ad4e25f0907e7d48df1ca3232f2da5d5.jpg

3e71b5831668ee458cf65fe39d60295e7d6.jpg

85d957d260531c5875ca63e4a567a98f0ea.jpg

当创建一个Vector容器,向其中添加一个元素是,首先会调用ensureCapacityHelper函数判断容器存储容量,如果容量不足则会调用grow函数扩容,上面我们说的扩展为原来的两倍,其实不是非常准确,因为当设置了扩容参数值,则就不是扩展为两倍了,而是原来的长度加上扩容参数值,默认情况下还是扩展为原来的两倍。

2.ArrayList

2f264dfddf928f1edeb0d47a0b24b70e339.jpg

ArrayList的初始容量大小也是10,和Vector的原理是完全一样的,只是不是线程安全的,我们这里主要看下ArrayList的扩容原理:

37cc95a9d19fc2af1b91451bbd37304ad49.jpg

fdafff46f017ae55c7375832c43d1dea90f.jpg

8c7c5c256d1bf27a06a855d0aa356c9405c.jpg

5d0f0aba7712478f5d094f66ce776c9c26e.jpg

f3663c53d5c86a069a936a04d236ff88051.jpg

ArrayList也是会先判断容器的容量大小,如果容量不足,则调用扩容方法grow函数,将容量扩展原来加原来一半也就是1.5倍

3.LinkedList

63b46b4da6e09bda869ab3b23f67eab4dbc.jpg

LinkedList是一个继承于AbstractSequentialList的双向链表,它也可以被当作堆栈、队列或双向队列进行操作。LinkedList实现 List 接口,能对它进行队列操作。LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。LinkedList 是非同步的(线程不安全的)。因为是双向链表,所以它的顺序访问会非常高效,而随机访问效率比较低。

d52d65a636e82213e2619217cc72f9f7a27.jpg

下面我们先看下LinkedList的添加元素的原理:

0f80bb356f615c7f47dbbb7199f9beda35a.jpg

e94aa06b0e246e3e896556d31cb2c0ea588.jpg

819735183bc5e1bb503681935b46f91fa28.jpg

我们从源码可以看到其实add添加元素的操作就是在容器的最后新增一个数据节点,具体分析:先把当前最后一个节点存档到l数据节点中,然后新增一个数据节点,这里有一个判断,如果l为空那就是说改链表是空的,这样这个新增元素即是第一个也是最后一个节点;如果l不为空,将当前最后一个节点变成新增的前一个节点,然后last存放新增节点使其变成最后一个元素节点,这样一个新增的操作就完成了。

5e3d3b0f36975c1c958e8a59d1c024a19c0.jpg

我们再来看看向制定位置添加数据节点的原理,看下面源码:

c5c9310f6e64f3871acf1b5e22fab6cdfe9.jpg

c002765edf33f064117494b8a4d5741af07.jpg

df8fbacf9cfcfcfda92138431db2db62147.jpg

f83865b53f90f723d1954cc957a97d9dbfe.jpg

首先是位置是否存在,添加元素的位置必须是大于等于0小于等于链表的大小;然后判断如果要添加元素的位置等于链表的大小,则该元素插入最有一个即可,否则在指点的位置前插入节点,先将该位置前的阶段存起来到pred,然后判断如果pred为空说明该链表是空的,则将新增数据节点写入第一个节点中,如果pred不为空,将原来的前一个节点的下一个节点指向新添加的节点,将新添加节点的下一个节点指向原来位置的下一个节点即可

4f7a3b8c0fe48d74b45d3c6f1c63c180eb5.jpg

接下来我们看下删除第一个节点元素:

c72e9ede7324df4e8f6c7a5778e79f95768.jpg

3e1bc6624df2c5d04610e7a7682b4a19707.jpg

删除时是先把原来的第一个节点的下一个节点存到first中,然后将第一个节点的指向都设置为null(删除),然后将first指向next就可以了,然后判断如果next为空则last设置为空,这时候整个链表也就是空的;反之释放next内存;然后将链表大小减小一个,将此列表已被结构修改的次数减一

接下来我们看下删除最后一个节点元素:

9959835f1cc2fdc496c3e528814b071ed55.jpg

75aba4991698e08bd02506e081818045503.jpg

删除最后一个元素是相对简单一些,删除最后一个节点,然后释放该节点指向前一个节点的指针空间和释放前一个节点指向下一个节点的指针空间,然后将链表大小减小一个,将此列表已被结构修改的次数减一

75f3e011051eaad9e3c1fc356234a7a6f49.jpg

 

转载于:https://my.oschina.net/zss1993/blog/1839421

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值