【JAVA SE】集合框架--LinkedList

与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

原创,转载请声明,谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值