LinkedList特点
- LinkedList和ArrayList一样,它们都是非线程安全的;并且都允许存放null值;
- LinkedList的底层是一个双向链表;所以它在进行插入、删除效率较高,而做查询操作效率较低;
- 它是一个链表,所以不存在容量不足的问题,因此没有扩容机制。
LinkedList的内部
可以发现,LinkedList的无参构造器中不执行任何操作。
其内部类Node:
private static class Node<E> {
E item; // 节点中的数据项
Node<E> next; // 当前节点的next指针,指向下一个节点对象
Node<E> prev; // 当前节点的prev指针,指向上一个节点对象
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
分析LinkedList源码查看add操作到底怎么执行的
在对上述大概了解过后,就可以深入查看其源码了。
先来看看这段代码:
LinkedList<Integer> list = new LinkedList<>();
list.add(1);
list.add(2);
我们先来分析在执行list.add(1);
方法时,它的底层是怎么做的?
使用debug,step into add
方法:
在add方法中,主要调用了一个linkLast
方法:
- 141:首先将Node类型的对象l指向我们的last对象,此时last为null,因为我们是第一次执行add操作,所以这里的对象l被赋值为null;
- 142:又去创建了一个节点newNode,将l对象作为prev,e(我们所add的对象)作为节点项,next为null;
- 143:将last指向newNode,也就是我们刚刚创建的节点;
- 144:此时的l为null,所以这里会进入if判断语句内,将fist也指向newNode节点对象,这个时候,我们链表大致如下图所示:
- 148~149:增加链表大小和修改次数。
继续往下看list.add(2)
方法:
前面还是不变,我们直接来到linkLast
方法:
分析:
-
141:创建Node类型的对象l指向我们的last对象,此时的last不再是null了,因为我们前面执行了一次
add(1)
操作,执行完这一步,链表如下图:
-
142:创建了一个节点newNode,将l对象作为prev(也就是将改Node对象的prev指向l节点对象),e(我们所add的对象)作为节点项,next为null,此时,链表如下图:
-
143:将last指向newNode;
-
144:指向到if判断时,此时l已经不为null了,所以去指向else语句,
l.next = newNode
,将l节点的next指向newNode;
这就是完整的LinkedList的add操作流程,就是这么妙。
LinkedList的remove方法底层怎么做的?
在LinkedList中有很多remove方法:
这里我们主要来看看remove()
这个方法。
再来看一段代码:
LinkedList<Integer> list = new LinkedList<>();
list.add(1);
list.add(2);
list.remove();
remove方法默认是删除链表的第一个元素。
可以看到,在remove方法中,主要执行了removeFirst
方法。
分析:
-
268:创建一个Node对象f,将first指向的节点对象赋值给它;
-
271:如果它不为空,则执行
unlinkFirst
方法;
分析:
-
173:取出f指向的节点中的item数据项;
-
174:创建一个Node对象next,将它指向f.next节点;
-
175~176:
f.item = null ; f.next = null
;
-
177:将first指向刚刚创建的next对象;
-
178:这里判断next此时不为null,所以会执行else语句,将next的prev置为null;
整理一下图:
执行到这里,你会神奇的发现,需要删除的这个节点元素已经完全脱离我们链表了。
- 182~184:将链表大小减去1,自增修改字数,返回被删除的元素;