zoukankan      html  css  js  c++  java
  • Java源码--LinkedList源码概述

    与ArrayList同为List,LinkedList却展现出不同的特性。作为java.util下的另一重要容器,我们下面来探究一下LinkedList的源码实现及特性分析。

    上篇文章讲述到,ArrayList用数组来存储数据,伴随数据量的变大,ArrayList动态扩充数组容量
    与之不同,LinkedList使用链表来存储数据,因此它在插入/删除数据方面有着天然的优势,而在读取指定位置的元素时,性能却不及ArrayList速度快

    LinkedList存储元素的变量如下:

    transient Node<E> first;
    

      存储数据的节点:Node类如下:

    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; //后节点
    	}
    }
    

     可以看出来,所有元素节点连在一起,构成了双向链表(prev指向前一个元素节点,next指向后一个元素节点)。所有数据,保存在Node的item中。相比于单向链表,双向链表提供了两种操作的方向,因此可以提供更快的遍历查找速度,更快的插入、删除元素速度(代价是提升了链表维护的难度,以及一点点存储引用的空间)。

    下面看一下LinkedList内部对于双向链表的维护:


    在此之前,需要知道的一点:LinkedList额外保存了first和last两个节点,分别指向首位和末尾,作为双向链表的两端入口。

    内部维护链表的一系列函数如下(private、protected):

    //用e构造Node,将数据插到首位
    private void linkFirst(E e)
    {
    	final Node<E> f = first; //取得首位节点node
    	final Node<E> newNode = new Node<>(null, e, f); //构造节点,item为e,prev为null,next指向当前的首位f
    	first = newNode; //将first指向newNode,以它作为新的首位
    	if (f == null)
    		last = newNode; //若之前的首位f不存在,则新的首位节点newNode在作为first的同时,也是last
    	else
    		f.prev = newNode; //若之前的首位f存在,则将f的prev指向新的首位节点newNode
    	size++;
    	modCount++;
    }
    
    //用e构造Node,将数据插到末尾,原理类似
    void linkLast(E e);
    
    //将节点插入到指定节点前
    void linkBefore(E e, Node<E> succ)
    {
    	final Node<E> pred = succ.prev; //取得原先succ之前的节点pred,现在需要将newNode插在succ之前,pred之后
    	final Node<E> newNode = new Node<>(pred, e, succ); //构造内容为e的newNode,prev指向pred,next指向succ
    	succ.prev = newNode; //将succ的prev指向newNode
    	if (pred == null)
    		first = newNode; //若pred为空,则将newNode置为first
    	else
    		pred.next = newNode; //将pred的next指向newNode
    	size++;
    	modCount++;
    }
    
    //将首位first节点从链表去掉
    private E unlinkFirst(Node<E> f);
    
    //将末位last节点从链表去掉
    private E unlinkLast(Node<E> l);
    
    //将任意位置的节点x从链表去掉
    E unlink(Node<E> x)
    {
    	//分别考虑prev与next为null和非null的情况,修复prev与next的指向
    }
    

      基于上述链表操作函数,LinkedList开放了如下接口(public)

    public E getFirst()
    {
    	//取得first内部的item,返回
    }
    
    public E getLast()
    {
    	//取得last内部的item返回
    }
    
    public E removeFirst()
    {
    	//调用unlinkFirst(Node<E> f), 将first从链表移除
    }
    
    public E removeLast()
    {
    	//调用unlinkLast(Node<E> l), 将last从链表移除
    }
    
    public void addFirst(E e)
    {
    	//将e插入到first之前
    }
    
    public void addLast(E e)
    {
    	//将e插入到last之后
    }
    
    public boolean add(E e)
    {
    	//调用linkLast(e),将e插入到last之后
    }
    
    public boolean remove(Object o)
    {
    	//从first开始遍历链表,找到o,移除节点
    }
    
    
    public int indexOf(Object o)
    {
    	//从first开始遍历节点,找到o,返回index
    }
    
    public int lastIndexOf(Object o)
    {
    	//从last开始反向遍历链表,找到o,移除链表
    }
    
    public boolean contains(Object o)
    {
    	//从first开始遍历链表,找到o,返回true;或者找不到o,返回false
    }
    
    public boolean addAll(Collection<? extends E> c)
    {
    	//1. 将集合c转成数组
    	//2. 遍历数组,对于每个元素,构造node,链在last后面
    }
    

      

    总结LinkedList用双向链表维护元素,相比于ArrayList提供了快速的插入、移除数据操作的同时,也比单向链表的遍历查找速度更快。但是,相比于ArrayList,LinkedList的查找指定index元素效率低(ArrayList使用数组存储数据,可以直接依据索引读取)。

  • 相关阅读:
    PTA(Advanced Level)1037.Magic Coupon
    PTA(Advanced Level)1033.To Fill or Not to Fill
    PTA(Basic Level)1020.月饼
    PTA(Advanced Level)1048.Find Coins
    PTA(Advanced Level)1050.String Subtraction
    PTA(Advanced Level)1041.Be Unique
    PTA(Basci Level)1043.输出PATest
    PTA(Basic Level)1039.到底买不买
    PTA(Basic Level)1033.旧键盘打字
    PTA(Advanced Level)1083.List Grades
  • 原文地址:https://www.cnblogs.com/xinxinBlog/p/9937651.html
Copyright © 2011-2022 走看看