LinkedList源码分析

本文详细介绍了Java中的LinkedList数据结构,包括其内部实现原理、基本操作方法及其应用场景。解释了双向循环链表的特点,并通过实例展示了如何进行添加、删除等操作。

LinkedList简介

LinkedList是List接口的一种实现,底层数据结构基于双向链表。除了List接口,它还实现了Queue和Deque接口。可能是因为以上特点,在代码的注释中才出现了这样饶有意味的嘱托。

This class is primarily useful if you need queue-like behavior. It may also be useful as a list if you expect your lists to contain zero or one element, but still require the ability to scale to slightly larger numbers of elements. In general, though, you should probably use ArrayList if you don't need the queue-like behavior.

如果你需要queue-like操作,或者你的list大部分情况下都只有01个元素,但是也有变大的需求,那么它可以用。否则建议使用ArrayList。

数据结构

每个数据都存在这个Link结构里,它是组成双向链表的基本结构,无论是叫Link、Entry还是Node,它都由装数据的data和指向前后节点的previous及next组成。

private static final class Link<ET> {
       ET data;

       Link<ET> previous, next;

       Link(ET o, Link<ET> p, Link<ET> n) {
           data = o;
           previous = p;
           next = n;
       }
   }

其实说双向链表并不十分准确,更精确的说法应是双向循环链表,因为它的首尾是相连的。一个典型的双向循环链表如下

这里写图片描述

初始化

因为LinkedList不涉及底层扩容,所以像ArrayList那样指定初始容量显然没有意义,LinkedList提供了有集合参数和没有参数
的两种构造方法,且有参的内部调用了无参,所以从无参数的构造方法说起更具意义。

public LinkedList() {
       voidLink = new Link<E>(null, null, null);
       voidLink.previous = voidLink;
       voidLink.next = voidLink;
   }

voidLink是一个全局变量

Link<E> voidLink;

人如其名,voidLink的data、previous、next三个参数都被初始化成了null,然后将p和n都指向了自己。
另外需要注意,虽然该构造方法里创建了voidLink节点,但是并没有将list的size属性+1。
我们执行如下语句初始化一个LinkedList

LinkedList<String> list = new LinkedList<>();

此时它的结构应该如下
这里写图片描述

add方法

list.add("w");
public boolean add(E object) {
        return addLastImpl(object);
    }

    private boolean addLastImpl(E object) {
        Link<E> oldLast = voidLink.previous;
        Link<E> newLink = new Link<E>(object, oldLast, voidLink);
        voidLink.previous = newLink;
        oldLast.next = newLink;
        size++;
        modCount++;
        return true;
    }

首先oldLast指向voidLink,此时oldLast就是voidLink。然后newLink的pre指向了oldLast,nxt指向了voidLink,其实都是指向了voidLink。如图1.
voidLink的previous指向newLink,如图2
oldLast也就是voidLink的next指向newLink如图3
这里写图片描述

这样add操作就完成了。
其实再分析一个LinkedList的指定位置插入方法,会很能体现出该方法基于双向链表的优雅之处。但总抓着add方法不放也确实乏味。remove方法是add方法的相反,在效果上和实现上都是如此,所以我打算换一碗汤,来分析一下remove方法,也能体会到LinkedList对数据的组织。

public E remove(int location) {
       if (location >= 0 && location < size) {
           Link<E> link = voidLink;
           if (location < (size / 2)) {
               for (int i = 0; i <= location; i++) {
                   link = link.next;
               }
           } else {
               for (int i = size; i > location; i--) {
                   link = link.previous;
               }
           }
           Link<E> previous = link.previous;
           Link<E> next = link.next;
           previous.next = next;
           next.previous = previous;
           size--;
           modCount++;
           return link.data;
       }
       throw new IndexOutOfBoundsException();
   }

我们假设此时list里又依次add了有”“i”、”n”、”d”三个字母,那么此时链表应如图
这里写图片描述
如果想要把”i”remove掉,那么执行如下代码即可

list.remoe(1);

第一个if是合法性检查,第二个if是看location是在前半部分还是后半部分,这样分情况查找速度会快。如果在前半部分则从voidLink开始通过next属性一路遍历,如果在后半部分则通过previous遍历。显然i在前半部分,执行完for循环如下图。
这里写图片描述

然后执行查查行如下,即给previous和next赋值
这里写图片描述

再执行叉叉行如下
这里写图片描述

虽然说i的n和p依然指向链表内的元素,但是无论通过next还是previous方向遍历都不会遍历到它了。也就是它不再被任何链表内的元素引用,所以它已经和链表没有关系了。整理一下如下
这里写图片描述

其他代码加注释如下

public class LinkedList<E> extends AbstractSequentialList<E> implements
        List<E>, Deque<E>, Queue<E>, Cloneable, Serializable {

    private static final long serialVersionUID = 876323262645176354L;

    transient int size = 0;

    transient Link<E> voidLink;
//  双向链表的数据结构的节点类
// 其属性有三个 数据,上一节点,下一节点。
    private static final class Link<ET> {
      // 数据
        ET data;
//      前后节点,是Link类型的引用,用以引用前后两个Link类型的数据。
//      真正的对象放在堆里,这两个引用放在栈里。
//      也就是说,这是一个属性。
        Link<ET> previous, next;
        // 构造方法
        // 疑问:一个Link类里,为什么能有Link属性?
        Link(ET o, Link<ET> p, Link<ET> n) {
            data = o;
            previous = p;
            next = n;
        }
    }

// 构造函数,构造一个只含有voidLink的List
    public LinkedList() {
           voidLink = new Link<E>(null, null, null);
           voidLink.previous = voidLink;
           voidLink.next = voidLink;
       }

    public LinkedList(Collection<? extends E> collection) {
        this();
        addAll(collection);
    }

// 默认插入在链表尾部
    @Override
    public boolean add(E object) {
        return addLastImpl(object);
    }

    private boolean addLastImpl(E object) {
        Link<E> oldLast = voidLink.previous;
        // 分两步实现双向链表的插入
        // 第一步:将object的p属性设置为oldLast,n属性设置为voidLink
        // 第二部:将voidLink的p属性设和oldLast的n属性设置为newLink
        Link<E> newLink = new Link<E>(object, oldLast, voidLink);
        voidLink.previous = newLink;
        oldLast.next = newLink;
        size++;
        modCount++;
        return true;
    }

// 指定位置插入
    @Override
    public void add(int location, E object) {
        if (location >= 0 && location <= size) {
            Link<E> link = voidLink;
            // 先进行查找
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            // 然后进行插入
            Link<E> previous = link.previous;
            Link<E> newLink = new Link<E>(object, previous, link);
            previous.next = newLink;
            link.previous = newLink;
            size++;
            modCount++;
        } else {
            throw new IndexOutOfBoundsException();
        }
    }

    //  在某个指定位置插入某个集合
    // 有一个遍历集合的操作
    @Override
    public boolean addAll(int location, Collection<? extends E> collection) {
        if (location < 0 || location > size) {
            throw new IndexOutOfBoundsException();
        }
        int adding = collection.size();
        if (adding == 0) {
            return false;
        }
        Collection<? extends E> elements = (collection == this) ?
                new ArrayList<E>(collection) : collection;

        Link<E> previous = voidLink;
        // 前后哪边近,从哪边遍历。提高性能。
        if (location < (size / 2)) {
            for (int i = 0; i < location; i++) {
                previous = previous.next;
            }
        } else {
            for (int i = size; i >= location; i--) {
                previous = previous.previous;
            }
        }
        Link<E> next = previous.next;
        for (E e : elements) {
          // 分两步插入到previous的后边,这样循环下来,所有的elements都挨个插入到previous后边
          // 第一步:e的p属性设置成previous
          // 第二部:previous的next属性设置为newLink
            Link<E> newLink = new Link<E>(e, previous, null);
            previous.next = newLink;
            previous = newLink;
        }
        // 然后把之前previous后边的部分链接上,就完成了添加。
        previous.next = next;
        next.previous = previous;
        size += adding;
        modCount++;
        return true;
    }

    //  直接插入在队尾部,比指定位置插入少一步寻找位置的操作。
    @Override
    public boolean addAll(Collection<? extends E> collection) {
        int adding = collection.size();
        if (adding == 0) {
            return false;
        }
        Collection<? extends E> elements = (collection == this) ?
                new ArrayList<E>(collection) : collection;

        Link<E> previous = voidLink.previous;
        for (E e : elements) {
            Link<E> newLink = new Link<E>(e, previous, null);
            previous.next = newLink;
            previous = newLink;
        }
        previous.next = voidLink;
        voidLink.previous = previous;
        size += adding;
        modCount++;
        return true;
    }

    /**
     * Adds the specified object at the beginning of this {@code LinkedList}.
     *
     * @param object
     *            the object to add.
     */
    public void addFirst(E object) {
        addFirstImpl(object);
    }

    private boolean addFirstImpl(E object) {
      // 因为是双向循环链表,所以voidLink的next就是插入前的first
        Link<E> oldFirst = voidLink.next;
        // 插入操作同样是分两步
        Link<E> newLink = new Link<E>(object, voidLink, oldFirst);
        voidLink.next = newLink;
        oldFirst.previous = newLink;
        size++;
        modCount++;
        return true;
    }

  // 疑问:是怎样清除的?
  // 由于LinkedList对象只持有voidLink
  // 把voidLink的p和n都指向自己以后,其余的链表就不可达了,但他们仍是一个整体。
  // 能被回收,但是其中一个数据也被引用呢?岂不是会造成都不能被回收?
    @Override
    public void clear() {
        if (size > 0) {
            size = 0;
            voidLink.next = voidLink;
            voidLink.previous = voidLink;
            modCount++;
        }
    }



    @Override
    public boolean contains(Object object) {
        Link<E> link = voidLink.next;
        if (object != null) {
            while (link != voidLink) {
                if (object.equals(link.data)) {
                    return true;
                }
                link = link.next;
            }
        } else {
            while (link != voidLink) {
                if (link.data == null) {
                    return true;
                }
                link = link.next;
            }
        }
        return false;
    }
// 一个一个往下寻找
    @Override
    public E get(int location) {
        if (location >= 0 && location < size) {
            Link<E> link = voidLink;
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }


    @Override
    public int indexOf(Object object) {
        int pos = 0;
        Link<E> link = voidLink.next;
        if (object != null) {
            while (link != voidLink) {
                if (object.equals(link.data)) {
                    return pos;
                }
                link = link.next;
                pos++;
            }
        } else {
            while (link != voidLink) {
                if (link.data == null) {
                    return pos;
                }
                link = link.next;
                pos++;
            }
        }
        // 记住如果没有索引到,就返回-1
        return -1;
    }

    //  先索引,索引到之后再删除。
    @Override
    public E remove(int location) {
        if (location >= 0 && location < size) {
            Link<E> link = voidLink;
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            Link<E> previous = link.previous;
            Link<E> next = link.next;
            previous.next = next;
            next.previous = previous;
            size--;
            modCount++;
            return link.data;
        }
        throw new IndexOutOfBoundsException();
    }

    @Override
    public boolean remove(Object object) {
        return removeFirstOccurrenceImpl(object);
    }


    @Override
    public E set(int location, E object) {
        if (location >= 0 && location < size) {
            Link<E> link = voidLink;
            if (location < (size / 2)) {
                for (int i = 0; i <= location; i++) {
                    link = link.next;
                }
            } else {
                for (int i = size; i > location; i--) {
                    link = link.previous;
                }
            }
            E result = link.data;
            link.data = object;
            return result;
        }
        throw new IndexOutOfBoundsException();
    }


    @Override
    public Object[] toArray() {
        int index = 0;
        Object[] contents = new Object[size];
        Link<E> link = voidLink.next;
        while (link != voidLink) {
            contents[index++] = link.data;
            link = link.next;
        }
        return contents;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] contents) {
        int index = 0;
        if (size > contents.length) {
            Class<?> ct = contents.getClass().getComponentType();
            contents = (T[]) Array.newInstance(ct, size);
        }
        Link<E> link = voidLink.next;
        while (link != voidLink) {
            contents[index++] = (T) link.data;
            link = link.next;
        }
        if (index < contents.length) {
            contents[index] = null;
        }
        return contents;
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值