一起看源码:深入List分支LinkedList,顺便说一下Vector


简介: LinkedList 是通过双向链表去实现的,他的数据结构具有双向链表结构的优缺点,既然是双向链表,那么它的顺序访问会非常高效,而随机访问效率比较低(没有下标)。

继承结构

在这里插入图片描述
Queue:队列接口,一端进另一端出。
Deque:双端队列接口,两端都能进,两端都能出。从而给LinkedList提供getFirst()和getLast(),addFirst(E e)和addLast(E e)方法
AbstractSequentialList:继承AbstractList,主要就是实现了get,set,add,remove,iterator等方法

LinkedList

LinkedList它包含一个非常重要的私有的静态内部类:Node(节点)。
说明:Node 是双向链表节点所对应的数据结构,它包括的属性有:
item:当前节点所包含的值
prev:上一个节点
next:下一个节点
在这里插入图片描述
除此之外LinkedList类中还有几个重要的属性:
int size:元素的个数
Node first:第一个元素节点
Node last:最后一个元素节点

由于 LinkedList 实现了 List 的接口,所有必然具备 List 的特性

LinkedList 的下标搜索get(int index)

我们都知道LinkedList是没有索引得啊,那么List接口中定义的一个方法 get (int index)在LinkedList 到底是如何实现得呢?
接下来我们一起看源码,一步步的探究一下:

假设我们传入索引为1,集合的大小为10吧。

1.找到LinkedList 类中的get(int index)方法
checkElementIndex(index):调这个方法就是对下标是否越界的检查,越界了就抛IndexOutOfBoundsException我们都知道。
这里传入的索引为1,肯定不越界的。直接进入下一步

在这里插入图片描述
2.进入node(int index)方法,传入1
在这里插入图片描述
自上往下解析:
if判断条件:index < (size >> 1)index为1,size为10。1<10/2肯定为true嘛。
进入if语句:拿到第一个元素的节点为x,然后进行了正序遍历(看else语句中是不是倒序遍历啊,他俩是相反的)
遍历:我们现在的index为1对吧。第一次遍历i=0;满足i<index,进行遍历操作。拿到第一个元素的下一个节点重新赋值给x,第二次遍历i=1;index=1是不是不满足i<index了,那么return x,就拿到我们需要的节点了。
3.返回到get方法中
node(index).item; 是不是就拿到该节点的元素值了。

总结:这就是LinkedList的get(int index)的操作。并不是通过索引来直接获取的,因为双向链表是没有元素索引的。所以为了提高效率,设计者采用了二分法来操作(如果要找的元素位置比size的一半要小,那么就从头开始遍历。反之就从尾开始倒序遍历)
set(int index, E element)的操作也是这样操作的
通过上面的总结,我们发现确实针对下标位(索引)的随机访问,数组的优势很明显

LinkedList 内部的其他操作

在 LinkedList 中具体实现,由于他的数据结构的原因,我们从头尾获取元素还是非常简单的。都知道first和last保存着第一个元素和最后一个元素。那么这两个变量是怎么被赋值的呢?不用想肯定在添加元素或者删除元素是都要重新给他赋值的。

这里就以add(E e)方法为例:假设是第一次添加元素

1.找到add方法
在这里插入图片描述
2,进入linkLast() 方法传入所添加的元素

在这里插入图片描述
自上往下解析:
Node l = last:将last赋值给l变量:此一次添加元素时,last还没赋值肯定为null即l也为null。
new Node<>(l, e, null):new了一个新的节点对象,l:上一个节点、e:添加的元素值、新增的元素肯定没有下一个节点吧,所以为null
last = newNode:将当前节点赋值给last最后一个节点。
if (l == null){ first = newNode;}:条件满足吧,所以当前节点也赋值给first第一个节点
size++;modCount++;这就不用说了吧(温馨提示:在ArrayList的文章中说的很详细)

注意:第一次添加元素,那么这个节点肯定是第一个也是最后一个,对吧

其他操作对照源码进行品读,其实看源码并没有想象的难,挑能看懂的看就行啦哈哈

浅析并发安全的却过时的 Vector

简介:Vector 的底层与我们的 ArrayList 类似.都是以动态数组的方式进行对象的存储

说到Vector,都知道是线程同步操作安全的,为什么呢?它底层的源码是怎样的?下面就来看一看:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
看了源码是不是感觉:卧槽只是加了synchronized关键字啊。其实Vector它就是这样子的。很多对外的方法都是用 Synchronized 关键字进行修饰的. 所以通过 vector 进行操作性能并不高.所以慢慢被放弃了。

List 接口实现类的并发安全保证

要保证 List 接口实现的集合对象在同步操作时能够线程安全.我们可以使用下面的方式:
List list = Collections.synchronizedList(new ArrayList());
最终都是返回 SynchronizedList 对象
在这里插入图片描述
SynchronizedList 中指定了Object mutex为锁对象,后续的操作中都用mutex对象来锁。
在这里插入图片描述
简单说一下Vector和SynchronizedList的区别:
1.扩容机制不同Vector扩容为原来的2倍长度,SynchronizedList和ArrayList一样扩容为原来1.5倍
2.SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类。
3.使用SynchronizedList的时候,进行遍历时要手动进行同步处理 。
4.SynchronizedList可以指定锁定的对象。

<<上一篇:深入List 分支 ——ArrayList
>>下一篇:深入HashMap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值