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

时间:2024-03-25 19:44:51


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

继承结构

一起看源码:深入List分支LinkedList,顺便说一下Vector
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:下一个节点
一起看源码:深入List分支LinkedList,顺便说一下Vector
除此之外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,肯定不越界的。直接进入下一步

一起看源码:深入List分支LinkedList,顺便说一下Vector
2.进入node(int index)方法,传入1
一起看源码:深入List分支LinkedList,顺便说一下Vector
自上往下解析:
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方法
一起看源码:深入List分支LinkedList,顺便说一下Vector
2,进入linkLast() 方法传入所添加的元素

一起看源码:深入List分支LinkedList,顺便说一下Vector
自上往下解析:
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,都知道是线程同步操作安全的,为什么呢?它底层的源码是怎样的?下面就来看一看:

一起看源码:深入List分支LinkedList,顺便说一下Vector
一起看源码:深入List分支LinkedList,顺便说一下Vector
一起看源码:深入List分支LinkedList,顺便说一下Vector
看了源码是不是感觉:卧槽只是加了synchronized关键字啊。其实Vector它就是这样子的。很多对外的方法都是用 Synchronized 关键字进行修饰的. 所以通过 vector 进行操作性能并不高.所以慢慢被放弃了。

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

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