jdk1.6集合源码阅读之LinkedList

      若是说ArrayList是基于数组实现的List,那么LinkedList是基于链表实现的List。java

1.定义

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

        能够看获得LinkedList继承了AbstractSequentialList。实现了List,Deque. 后面两个和ArrayList同样,说明能够被克隆和序列化。数组

          而AbstractSequentialList基础自AbstractList,并且还从新实现了get,set,add,remove,等等方法。数据结构

AbstractSequentialList的代码以下:函数

package java.util;


public abstract class AbstractSequentialList<E> extends AbstractList<E> {
   
    protected AbstractSequentialList() {
    }

   
    public E get(int index) {
        try {
            return listIterator(index).next();
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }

 
    public E set(int index, E element) {
	try {
	    ListIterator<E> e = listIterator(index);
	    E oldVal = e.next();
	    e.set(element);
	    return oldVal;
	} catch (NoSuchElementException exc) {
	    throw new IndexOutOfBoundsException("Index: "+index);
	}
    }

   
    public void add(int index, E element) {
	try {
	    listIterator(index).add(element);
	} catch (NoSuchElementException exc) {
	    throw new IndexOutOfBoundsException("Index: "+index);
	}
    }

    public E remove(int index) {
	try {
	    ListIterator<E> e = listIterator(index);
	    E outCast = e.next();
	    e.remove();
	    return outCast;
	} catch (NoSuchElementException exc) {
	    throw new IndexOutOfBoundsException("Index: "+index);
	}
    }


   
    public boolean addAll(int index, Collection<? extends E> c) {
	try {
	    boolean modified = false;
	    ListIterator<E> e1 = listIterator(index);
	    Iterator<? extends E> e2 = c.iterator();
	    while (e2.hasNext()) {
		e1.add(e2.next());
		modified = true;
	    }
	    return modified;
	} catch (NoSuchElementException exc) {
	    throw new IndexOutOfBoundsException("Index: "+index);
	}
    }


 
    public Iterator<E> iterator() {
        return listIterator();
    }

    
    public abstract ListIterator<E> listIterator(int index);
}

而Dqueue接口 是一个双向队列,也就是既能够先入先出,又能够先入后出,再直白一点就是既能够在头部添加元素又在尾部添加元素,既能够在头部获取元素又能够在尾部获取元素。看下Deque的定义this

public interface Deque<E> extends Queue<E>

 

 

2.底层存储   

private transient Entry<E> header = new Entry<E>(null, null, null);
 private transient int size = 0;

size和ArrayList里面的size同样,记录容器元素的个数。那这个Entry类型的变量header是个什么鬼。spa

Entry是个内部类,来描述链表的节点的信息,代码以下:.net

//描述链表节点的类
private static class Entry<E> {
	E element; //存储的对象
	Entry<E> next;//链表的下一个节点元素
	Entry<E> previous;//链表的上一个节点元素

	Entry(E element, Entry<E> next, Entry<E> previous) {
	    this.element = element;
	    this.next = next;
	    this.previous = previous;
	}
}

能够看得出ArrayList底层是采用双向链表来实现的。指针

数据结构双向链表是包含两个指针的,pre指向前一个节点,next指向后一个节点,可是第一个节点head的pre指向null,最后一个节点的tail指向null。code

3.构造函数

//默认构造
public LinkedList() {
    链表的头和尾都指向了本身
    header.next = header.previous = header;
}

//将一个集合的元素来构造本身,这些元素按其 collection 的迭代器返回的顺序排列
public LinkedList(Collection<? extends E> c) {
	this();
	addAll(c);
}

从默认构造函数能够看得出这是一个双向循环链表,若是是双向不循环链表的话,应该是:对象

header.next=header.previous=null。

4.增长

        有8个增长,5个增长是实现Dqueue里面的添加函数,其他3个是实现List接口里面的添加函数,而后实现Dqueue基本是调用实现List里面的添加函数,因此咱们使用的时候直接调用List里面的添加函数便可。能够少压一次栈。

//这5个add是LinkedList实现Dqueue里面的添加函数,其实都是调用实现List接口里面的函数

//将元素加到第一个,能够看获得,是加到header的后面,由于header就是一个空头,里面没有存储元素
public void addFirst(E e) {
	addBefore(e, header.next);
}
//将元素加列表的尾部
public void addLast(E e) {
	addBefore(e, header);
}

//封装链表插入操做,分两步走
//1.其实就是在构造的时候,本身的netx和previous指好
//2.本身的前节点的next指向本身,本身后结点的pre指向本身。
private Entry<E> addBefore(E e, Entry<E> entry) {
	Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);

    //本身的前节点的next指向本身
	newEntry.previous.next = newEntry;
    //本身后结点的pre指向本身
	newEntry.next.previous = newEntry;
    //记数加1
	size++;
	modCount++;
	return newEntry;
}

 public boolean offer(E e) {
        return add(e);
}


public boolean offerFirst(E e) {
        addFirst(e);
        return true;
}

   
public boolean offerLast(E e) {
        addLast(e);
        return true;
}

//下面三个增长是实现List接口里面的添加函数
//将元素加列表的尾部
public boolean add(E e) {
	addBefore(e, header);
    return true;
}

//添加一个集合元素到list中
public boolean addAll(Collection<? extends E> c) {
         //其实仍是调用在指定位置添加一个集合到list中
        return addAll(size, c);
}

//在指定位置添加一个集合元素到list中   
public boolean addAll(int index, Collection<? extends E> c) {
        //检查下标是否越界 
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
        //将集合转换为数组
        Object[] a = c.toArray();
        //要增长元素的长度
        int numNew = a.length;
        if (numNew==0)
            return false;
	modCount++;

        //找出要插入元素的先后节点

        //找出其后节点,若是位置和size大小相等(为何不是size-1,由于header占了一位),则他的下一个节点是header
        //不然要查找index位置的节点,这个就是他的后一个节点
        Entry<E> successor = (index==size ? header : entry(index));
        //他的前一个节点,就是index位置的前一个节点。
        Entry<E> predecessor = successor.previous;
	    for (int i=0; i<numNew; i++) {
            Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
            //将它前一个节点的next指向本身
            predecessor.next = e;
            //后面的元素插入到这个元素的后面
            predecessor = e;
        }
        //将index位置的节点的前指针指向本身这样就完成了链表的操做。
        successor.previous = predecessor;

        size += numNew;
        return true;
}

双向链表的增长比起数组的增长稍微是要麻烦的理解一点,本身画图应该不难理解,总结起来,就是一句话主要就是插入会改变前节点的next和后一节点的pre,主要把前一节点的next指向本身,后一节点的pre指向本身,便可。

5.删除

//删除第一个容器元素
public E removeFirst() {
	return remove(header.next);
}

//删除最后一个容器元素
public E removeLast() {
	return remove(header.previous);
}

//删除指定的容器元素
public boolean remove(Object o) {
        if (o==null) {
            for (Entry<E> e = header.next; e != header; e = e.next) {
                if (e.element==null) {
                    remove(e);
                    return true;
                }
            }
        } else {
            //循环遍历节点,而后找到节点,而后删除。时间复杂度O(n)
            for (Entry<E> e = header.next; e != header; e = e.next) {
                if (o.equals(e.element)) {
                    remove(e);
                    return true;
                }
            }
        }
        return false;
}

//删除指定位置的容器元素
public E remove(int index) {
    //entry(index)是找到这个节点
    return remove(entry(index));
}

//删除指定的节点,供本身调用
private E remove(Entry<E> e) {
	if (e == header)
	    throw new NoSuchElementException();

        E result = e.element;
    //将本身的前一节点的后指针执行本身的后一节点
	e.previous.next = e.next;
    //讲本身后一节点的前指针指向本身的前节点
    //讲本身置为null,给gc回收
	e.next.previous = e.previous;
        e.next = e.previous = null;
        e.element = null;
    //大小减一
	size--;
	modCount++;
        return result;
}

删除则要简单一点,删除了本身以后,主要

将本身的前一节点的后指针执行本身后一节点
讲本身后一节点的前指针指向本身的前节点

并且能够看到remove(Object o)时间负责度为O(n),而remove(int)的时间复杂度度为O(n/2),由于里面用到了二分查找的办法。因此删除的时候要注意了,要选用正确的方法删除。(其实我转载了一篇博客专门介绍这个LinkenList的局限。)因此之前所说的增删快的删有时也是很慢的。

6.修改容器元素的值

public E set(int index, E element) {
        Entry<E> e = entry(index);
        E oldVal = e.element;
        e.element = element;
        return oldVal;
}

这个比较简单,查找到,而后修改便可

7.查找

         咱们知道链表和数组相比,查找比数组要慢的很是多,数组直接定位,而链表每次咱们只能拿到一个头部,因此无论找什么,咱们都要从头开始遍历起,而LinkedList使用了双向循环链表,这样遍历起来就会快不少,既能够从头日后找,又能够从后往前找。直到找到index位置。

//查找指定index的链表里面元素
public E get(int index) {
        return entry(index).element;
}

//这个方法很重要,基本上查找都是使用这个方法来进行查找。
//这儿就显示双向循环链表的好处,既能够从头日后遍历,又能够从后往前遍历
//这儿使用了二分查找的方法,效率要高不少
private Entry<E> entry(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+size);
        Entry<E> e = header;
        //若是下标小于size的通常,就从头日后遍历,找到元素
        if (index < (size >> 1)) {
            for (int i = 0; i <= index; i++)
                e = e.next;
        } else {不然从后往前遍历
            for (int i = size; i > index; i--)
                e = e.previous;
        }
        return e;
}

 到这里咱们明白,基于双向循环链表实现的LinkedList,经过索引Index的操做时低效的,index所对应的元素越靠近中间所费时间越长。而向链表两端插入和删除元素则是很是高效的(若是不是两端的话,都须要对链表进行遍历查找)。

8.其他的LinkedList的操做

8.1 查找元素是否在容器中

public boolean contains(Object o) {
        return indexOf(o) != -1;
}

public int indexOf(Object o) {
        int index = 0;
        if (o==null) {
            for (Entry e = header.next; e != header; e = e.next) {
                if (e.element==null)
                    return index;
                index++;
            }
        } else {
            for (Entry e = header.next; e != header; e = e.next) {
                if (o.equals(e.element))
                    return index;
                index++;
            }
        }
        return -1;
}

public int lastIndexOf(Object o) {
        int index = size;
        if (o==null) {
            for (Entry e = header.previous; e != header; e = e.previous) {
                index--;
                if (e.element==null)
                    return index;
            }
        } else {
            for (Entry e = header.previous; e != header; e = e.previous) {
                index--;
                if (o.equals(e.element))
                    return index;
            }
        }
        return -1;
}

要遍历,低效。

打完收工。

相关文章
相关标签/搜索