LinkedList源码分析1

本文详细介绍了LinkedList的特性,包括其作为双向链表和双端队列的实现,以及克隆和序列化能力。文章通过源码解读了LinkedList的基本操作,如获取、添加、删除首尾元素,以及链表节点的结构和操作。同时,讨论了contains、size、add、remove等方法的工作原理,并展示了clear方法如何清空集合。
摘要由CSDN通过智能技术生成

1 LinkedList介绍

在这里插入图片描述

1 LinkedList继承了AbstractSequentialList,实现了接口List。是一个双向链表的实现。
2 LinkedList实现了接口Deque。表示实现了双端队列。可以FIFO。也可以LIFO(后进先出)
3 LinkedList实现了接口Cloneable、Serializable,表示可以克隆。也可以序列化。

2 属性介绍

/**
 * 元素的个数
 */
transient int size = 0;

/**
 * 指向第一个结点的指针
 */
transient Node<E> first;

/**
 * 指向最后一个结点的指针
 */
transient Node<E> last;

3 构造启

	//默认的构造器
	public LinkedList() {
	}

    /**
     * 构造一个包含元素的集合。
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

4 链表结点类结构

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;
        }
    }

在这里插入图片描述

5 源码解读

5.1 E getFirst() 获取第一个元素

   /**
   * 获取第一个元素。直接将头指针赋值给当前结点,然后当前结点返回存储的数据item。如果为空,则报NoSuchElementException异常。
   */
   public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

5.2 E getLast() 获取最后一个元素

 /**
   * 获取最后一个元素。直接将尾指针赋值给当前结点,然后当前结点返回存储的数据item。如果为空,则报NoSuchElementException异常。
   */
public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
}

5.3 E removeFirst() 删除第一个元素

 /**
   * 删除第一个元素。直接将头指针赋值给当前结点,然后解除结点之间的联系。调用unlinkFirst()方法接触引用
   */
public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
}

下面来看下unlinkFirst()方法。

private E unlinkFirst(Node<E> f) {
        // 获取删除结点的数据,作为最后的返回
        final E element = f.item;
        //获取删除结点的下一个结点
        final Node<E> next = f.next;
        //将删除结点的数据和后继设置为null,有利于垃圾回收
        f.item = null;
        f.next = null; // help GC
        //将删除结点的后继设置为头结点。
        first = next;
        //设置前驱为空。然后元素个数-1
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        //集合修改的次数。在迭代器迭代集合的时候使用
        modCount++;
        return element;
    }

5.4 E removeLast() 删除最后一个元素

 public E removeLast() {
 		//将尾结点赋值给当前结点,然后解除尾结点的引用。
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

调用unlinkLast实现了解除尾结点之间的引用。我们来看下该方法的实现。

private E unlinkLast(Node<E> l) {
        final E element = l.item;
        //获取删除结点的前驱
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // 方法GC
        //将前驱设置为尾结点
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

5.5 addFirst(E e)、addLast(E e) 添加元素

//头插
public void addFirst(E e) {
        linkFirst(e);
}
//尾插
public void addLast(E e) {
        linkLast(e);
}

分别调用linkFirst和linkLast实现。我们看看这两个方法。

 private void linkFirst(E e) {
 		//获取头结点
        final Node<E> f = first;
        //创建结点。设置前驱为null。后继为f。也就是插入之前的头结点
        final Node<E> newNode = new Node<>(null, e, f);
        //将创建的新节点赋值给头结点。
        first = newNode;
        if (f == null)
        	//如果当前结点为空,那么新结点就是尾结点。
            last = newNode;
        else
        	//将新节点设置为当前结点的前驱
            f.prev = newNode;
        //元素个数+1
        size++;
        modCount++;
    }

    /**
     * Links e as last element.
     */
    void linkLast(E e) {
    	//获取尾结点
        final Node<E> l = last;
        //创建新结点。前驱为插入之前的尾结点。后继为空
        final Node<E> newNode = new Node<>(l, e, null);
        //新结点设置为尾结点
        last = newNode;
        if (l == null)
            first = newNode;
        else
        	//设置新节点为插入之前的后继结点
            l.next = newNode;
        size++;
        modCount++;
    }
5.6 boolean contains(Object o) 判断是否包含元素
public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

调用indexOf()方法实现。下面看下该内部方法。

public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
        	//遍历链表。如果当前对象为空,返回索引。
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
        	//遍历链表。如果当前对象equals结点元素,返回索引。
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }
5.7 int size() 获取链表的长度
public int size() {
        return size;
}
5.8 boolean add(E e) 添加元素

默认是尾插。

public boolean add(E e) {
        linkLast(e);
        return true;
    }
5.9 boolean remove(Object o) 删除元素
public boolean remove(Object o) {
		//元素为空的情况
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
        //元素不为空的情况
        //遍历链表,找到元素,然后解除链表结点之间的引用。调用了unlink来解除
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

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) {
            first = next;
        } else {
        //将当前删除结点的后继结点设置为当前删除结点的后继结点。
            prev.next = next;
            //并且将当前结点的前驱结点设置为空。
            x.prev = null;
        }
		//后继结点为空,表示为尾结点
        if (next == null) {
        	//设置前驱为尾结点
            last = prev;
        } else {
        	//后继结点不为空。将删除元素的前驱设置为后继结点的前驱。后继结点设置为空
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        //元素个数-1
        size--;
        modCount++;
        return element;
    }

5.10 clear()清空集合。

遍历每一个结点,然后将所有结点元素置为空。结点置为空。集合元素个数设置为0

public void clear() {
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值