LinkedList实现类源码解析

我们为什么需要LinkedList?

  LinkedList并不是通过数组实现的,而是一种双向链表的结构来实现。这种结构的特点为:查询效率低,增删效率高,线程不安全

  所有,增加或删除元素较多时,我们选用LinkedList。

基本实现

双向链表

  LinkedList是通过双向链表实现的,首先,我们得了解双向链表。双向链表的结构如下图:
在这里插入图片描述
  链表是一个一个的节点组成的,在双向链表中,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。

代码实现

  双向链表中的节点实现源码如下:

    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类中有如下声明:

    transient int size = 0; //用于记录元素个数,初始值是0

    transient Node<E> first; //前指针,用于记录第一个节点

    transient Node<E> last; //后指正,用于记录最后一个节点

增、刪、改、查

  容器嘛,用来存储数据的,那就离不开”增、刪、改、查“这里四个字。那就来逐一看LinkedList是如何实现实现的。

  双向链表中增加数据,就是在最后一个节点,再挂上一个节点。旧最后节点的后指正指向新节点,新节点的前指针指向旧节点。源码:

    public boolean add(E e) {
        linkLast(e);  //调用linkLast方法
        return true;
    }
    
    void linkLast(E e) {
        final Node<E> l = last; //取出目前最后一个节点
        final Node<E> newNode = new Node<>(l, e, null); //初始化新节点
        last = newNode;  //新节点,就成为了最后一个节点
        if (l == null) //最后一个节点为null,说明是此时还没有节点
            first = newNode; //那么此节点,也同时为第一个节点
        else
            l.next = newNode;  //旧节点的后指针,指向新节点
        size++;            //用于记录元素个数的size加1
        modCount++;
    }

  双向链表的删除,其实就是移除,将节点移除链表。如果是移除中间的节点,则:被移除节点前一个节点的后指针,指向被移除节点的后一个节点;被移除节点后一个节点的前指针,指向被移除节点的前一个节点。源码如下:

    public E remove(int index) {
        checkElementIndex(index);    //检查确保输入的索引为正常合理值
        return unlink(node(index));  // 调用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;  //取出被移除节点的前一个节点

        if (prev == null) {  //前一个节点为null,说明被移除节点是头节点
            first = next;  //被移除后,头节点应该为被移除节点的后一个节点
        } else {
            prev.next = next;  //被移除节点前一个节点的后指针,指向被移除节点的后一个节点
            x.prev = null;  //被移除节点的前指针置空
        }

        if (next == null) { //前一个节点为null,说明被移除节点是尾节点
            last = prev;  //被移除后,头节点应该为被移除节点的前一个节点
        } else {
            next.prev = prev; //被移除节点后一个节点的前指针,指向被移除节点的前一个节点
            x.next = null;   //被移除节点的后指针置空
        }

        x.item = null;   //被移除节点的存储内容置空
        size--;   //用于记录元素个数的size减1
        modCount++;
        return element;
    }

  LinkedList的增删容易,改和查就比较麻烦了(主要是执行次数上升),需要循环多次来找到指定位置,再进行操作。源码如下:

    public E set(int index, E element) {
        checkElementIndex(index);  //检查确保输入的索引为正常合理值
        Node<E> x = node(index); //调用node方法,找到指定节点
        E oldVal = x.item;    
        x.item = element;    //修改节点存储的内容
        return oldVal;
    }

    Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {   //2分法
            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;
        }
    }

  “改”中,其实就是先查后改的,所有和改类似,查的源码如下:

    public E get(int index) {
        checkElementIndex(index);  //检查确保输入的索引为正常合理值
        return node(index).item;  //返回节点里存储的数据内容
    }

    Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {   //2分法
            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;
        }
    }

toString

  源码与ArrayList大致相似,主要是对双相链表的遍历,我这里手写了遍历,便于理解:

	@Override
	public String toString() {
		StringBuilder   sb = new StringBuilder("[");
		Node temp = first;  //从头节点开始
		while(temp!=null){   //一直取到空为止
			sb.append(temp.element+",");
			temp = temp.next;  //取该节点的下一个节点,继续循环
		}
		sb.setCharAt(sb.length()-1, ']'); 
		
		return sb.toString();
	}	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值