与ArrayList不同的是,LinkedList是基于链表实现的。所以这里要简单的说下数据结构,解释完数据结构LinkedList的源码实现看起来就很容易了。
简单来说,一般将数据结构分为两类:线性数据结构和非线性数据结构。线性数据结构有:线性表、栈、队列、串、数组、和文件;非线性结构有树和图。
线性表按存储结构可以分为顺序表和链表。顺序表是内存中地址连续存放的数据结构,而链表在内存地址中不是连续的。链表在java中每个节点存放着下一个节点的地址引用。
一维数组就是顺序存放的顺序表。
ArrayList的实现就是顺序表,而LinkedList的实现是链表。并切,LinkedList是一个双向链表。先来解释一下单向链表
下面用代码来实现下单向链表的实现
public class Node { public Node next;// 下一个节点 public Object data;// 数据 // 构造方法 public Node(Object data) { this.data = data; } }
Node对象代表一个节点 ,当一个列表有三个节点用代码实现
public static void main(String[] args) { Node node1=new Node("node1"); Node node2=new Node("node2"); Node node3=new Node("node3"); node1.next=node2; node2.next=node3; System.out.println(node1.next.next);//根据node1获取node3 Node node4=new Node("node4"); //将新元素node4插入node1与node2之间 node1.next=node4; node4.next=node2; //删除节点node2 node4.next=node3; }
以上分别用代码实现了链表的查找、添加、删除操作。
下面看下双向链表
双向链表中每个节点结构为
public class Node2 { Node2 prev;//指针域中前驱 Node2 next;//指针域中后继 Object data;//数据域 public Node2(Node2 current){ prev = current; next = current; } //双链表前驱后继及数字域 public Node2(Object d, Node2 p,Node2 n){ this.data = d; this.prev = p; this.next = n; } public Node2 getPrev() { return prev; } public void setPrev(Node2 prev) { this.prev = prev; } public Node2 getNext() { return next; } public void setNext(Node2 next) { this.next = next; } }
相比与单向链表,双向链表中节点不仅要知道它的下一个节点是谁,也要知道他的上一个节点是谁。同样以链表初始3个节点为例
public static void main(String[] args) { Node2 node1=new Node2("node1"); Node2 node2=new Node2("node2"); Node2 node3=new Node2("node3"); node1.next=node2; node1.prev=node3; node2.next=node3; node2.prev=node1; node3.next=node1; node3.prev=node2; //向node1之后插入新节点node4 Node2 node4=new Node2("node4"); node1.next=node4; node4.prev=node1; node4.next=node2; node2.prev=node4; //删除节点node2 node4.next=node3; node3.prev=node4; }
双向链表的插入,总结起来就是:先搞定s的前驱后继,再搞定后结点的前驱,最后解决前驱结点的后继
经过单向列表和双向列表的插入、删除操作再看LinkedList的源码实现就很容易理解了。下面看下LinkinList源码,以下中文注释
是我注释的,英文为源码官方注释
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { //声明初始list初始长度为0 transient int size = 0; /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */
//声明头节点 transient Node<E> first; /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */
//声明尾节点 transient Node<E> last;
//Node类的声明,是否跟之前写的双向列表的Node类很像?有前驱和后驱明显是一个双向链表
private static class Node<E> { E item;//节点中真正要存放的数据 ,可以暂且看做是一个Object类型的数据 Node<E> next;//后驱,表示当前节点指向的下一个节点 Node<E> prev;//前驱,表示当前节点指向的上一个节点 Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
下面以插入方法为例
/** * Appends the specified element to the end of this list. * * <p>This method is equivalent to {@link #addLast}. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */
//向LinkedList中插入一个Object类型的对象,并返回一个true public boolean add(E e) {
//调用linkLast(E e)方法,执行具体的插入操作,从方法名可知,此处插入默认使用的是向末尾插入元素 linkLast(e); return true; }
/** * Links e as last element. */
//向LinkedList末尾插入一个元素 void linkLast(E e) {
//首先将原来末尾的节点last赋值给一个新的节点l,新节点中保存了原来的前驱及数据 final Node<E> l = last;
//实例化一个新节点,前驱指向l,也就相当于在原来的链表末尾追加了一个节点 final Node<E> newNode = new Node<>(l, e, null);
//将newNode节点的赋值给last节点,此时last节点的前驱为l节点,l节点其实就是之前的last节点。
// 此时实现让新增节点成为最后一个节点的目的
last = newNode;
//如果l节点为空说明,未追加节点之前last节点为空,也就是之前的链表为空,此时新链表中头结点也为
// newNode,因为链表中就一个节点if (l == null ) first = newNode;
//否则,前节点不为空,自然要将前节点的后驱指向新加入的节点 else l.next = newNode; size++; modCount++; }
对比之前写的双向链表的例子,虽然代码实现不完全一样,但是双向链表的思想是完全一样的。
只要掌握了思想,无论是什么语言实现c或者java,或者实现方式不同,或者LinkedList中其他api方法增加、查找等
都没有任何难度。
ArrayList和LinkedList比较
1.ArrayList底层使用数组实现,LinkedList底层使用双向链表实现
2.当执行删除,添加元素操作时使用LinkedList效率更高,因为LinkedList并不需要移动元素,而ArrayList需要将要插入位置之后的所有元素移动位置。
3.当执行查询操作时,使用ArrayList效率更高,因为ArrayList底层数组实现内存中连续地址存放,只需要根据下标就能直接找到
元素在内存中的位置;而LinkedList在内存中的存放时不连续的,因此查找某个元素时需要多次next操作去查找目标元素内存地址。
jdk版本1.8 。之前看过1.6的源码与1.8实现有所不同
ps:图都是从大神们博客拽的,实在是懒,如有冒犯请见谅。已经快12点了,要去睡觉了。写的不对的地方,请指点。qq:29138386
原创,转载请声明,谢谢