实现List接口与Deque接口双向链表,实现了列表的所有操作,并且允许包括null值的所有元素,对于LinkedList定义我产生了如下疑问:
下文将围绕这两个问题进行,去探寻LinkedList内部的奥秘,以下源码是基于JDK1.7.0_79。
LinkedList的类的结构如下图所示:
通过上图可以看出,LinkedList继承的类与实现的接口如下:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
//长度
transient int size = 0;
//指向头结点
transient Node<E> first;
//指向尾结点
transient Node<E> last;
}
如上源码中为LinkedList中的基本属性,其中size为LinkedList的长度,first为指向头结点,last指向尾结点,Node为LinkedList的一个私有内部类,其定义如下,即定义了item(元素),next(指向后一个元素的指针),prev(指向前一个元素的指针)
private static class Node<E> {
//元素
E item;
//指向后一个元素的指针
Node<E> next;
//指向前一个元素的指针
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
那么假如LinkedList中的元素为[“A”,”B”,”C”],其内部的结构如下图所示
可以看出一个节点中包含三个属性,也就是上面源码中定义的属性,可以清晰的看出LinkedList底层是双向链表的实现
在源码中,LinkedList主要提供了两个构造方法,
在2.2.1中的LinkedList内部结构图,可以清晰的看出LinkedList双向链表的实现,下面将通过源码分析如何在双向链表中添加和删除节点的
通常我们会使用add(E e)方法添加元素,通过源码我们发现add(E e)内部主要调用了以下方法 //在链表的最后添加元素
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
其实通过源码可以看出添加的过程如下
LinkedList中提供了两个方法删除节点,如下源码所示
//方法1.删除指定索引上的节点
public E remove(int index) {
//检查索引是否正确
checkElementIndex(index);
//这里分为两步,第一通过索引定位到节点,第二删除节点
return unlink(node(index));
}
//方法2.删除指定值的节点
public boolean remove(Object o) {
//判断删除的元素是否为null
if (o == null) {
//若是null遍历删除
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
//若不是遍历删除
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
通过源码可以看出两个方法都是通过unlink()删除,在方法一种有个方法要介绍下,就是node(index)该方法的作用就是根据下标找到对应的节点,要是本人去写这个方法肯定是遍历到index找到对应的节点,而JDK提供的方法如下所示
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
下面会详细介绍unlink()方法的源码,这是删除节点最核心的方法
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//删除的是第一个节点,first向后移动
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
//删除的是最后一个节点,last向前移
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
至此介绍了LinkedList添加、删除元素的内部实现。
在ArrList详解中讲解了ArrayList的相关的内容,下面将对ArrayList与LinkedList进行对比,主要从以下方面进行
下面通过代码去比较下ArrayList与LinkedList在性能方面的差别,代码如下
public class ListPerformance {
private static ArrayList<String> arrayList= new ArrayList<String>();
private static LinkedList<String> linkedList = new LinkedList<String>();
/**
* 插入数据
* @param list
* @param count
*/
public static void insertElements(List<String> list, int count){
Long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
list.add(String.valueOf(i));
}
Long endTime = System.currentTimeMillis();
System.out.println("insert elements use time: " +(endTime-startTime) + " ms");
}
/**
* 删除元素
* @param list
* @param count
*/
public static void removeElements(List<String> list, int count){
Long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
list.remove(0);
}
Long endTime = System.currentTimeMillis();
System.out.println("remove elements use time: " +(endTime-startTime) + " ms");
}
/**
* 获取元素
* @param list
* @param count
*/
public static void getElements(List<String> list, int count){
Long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
list.get(i);
}
Long endTime = System.currentTimeMillis();
System.out.println("get elements use time: " +(endTime-startTime) + " ms");
}
/**
* 删除元素第二种实现
* @param list
* @param count
*/
public static void removeElements2(List<String> list, int count){
Long startTime = System.currentTimeMillis();
for (int i = count-1; i > 0; i--) {
list.remove(i);
}
Long endTime = System.currentTimeMillis();
System.out.println("remove elements use time: " +(endTime-startTime) + " ms");
}
public static void main(String[] args){
System.out.println("arrayList test");
insertElements(arrayList,100000);
getElements(arrayList,100000);
removeElements(arrayList,100000);
System.out.println("linkedList test");
insertElements(linkedList,100000);
getElements(linkedList,100000);
removeElements(linkedList,100000);
System.out.println("arrayList test2");
insertElements(arrayList,100000);
getElements(arrayList,100000);
removeElements2(arrayList,100000);
System.out.println("linkedList test2");
insertElements(linkedList,100000);
getElements(linkedList,100000);
removeElements2(linkedList,100000);
}
结果如下图所示,可以看出
~
自己动手实现一个这样的LinkedList:
public class MyLinkList<T> implements Iterable<T> {
private int size;
private int modeCount = 0;
private Node<T> beginMarker;
private Node<T> endMarker;
public MyLinkList() {
doClear();
}
private void doClear() {
size = 0;
modeCount++;
beginMarker = new Node<T>(null, null, null);
endMarker = new Node<T>(null, beginMarker, null);
beginMarker.next = endMarker;
}
public boolean add(T data) {
Node<T> node = new Node<>(data, endMarker.prev, endMarker);
endMarker.prev.next = node;
endMarker.prev = node;
modeCount++;
size++;
return true;
}
public T remove(int index) {
Node<T> node = getNode(index);
node.prev.next = node.next;
node.next.prev = node.prev;
size--;
modeCount--;
return node.data;
}
public Node<T> getNode(int index) {
Node<T> p;
if (index < 0 | index >= size) {
throw new IndexOutOfBoundsException("out of index");
}
if (index < size / 2) {
//从左边开始找
p = beginMarker.next;
for (int i = 0; i < index; i++) {
p = p.next;
}
} else {
//从右边开始找
p = endMarker;
for (int i = size; i > index; i--) {
p = p.prev;
}
}
return p;
}
public T get(int index) {
return getNode(index).data;
}
@Override
public Iterator<T> iterator() {
return null;
}
@Override
public void forEach(Consumer<? super T> action) {
}
@Override
public Spliterator<T> spliterator() {
return null;
}
static class Node<T> {
private T data;
private Node<T> prev;
private Node<T> next;
public Node(T data, Node<T> prev, Node<T> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
}
原文链接:
https://www.fangzhipeng.com/javainterview/2019/03/10/collections-linkedlist.html