【源码解析】JDK源码之LinkedList

LinkedList源码,基于JDK1.6.43

有序的列表,其内部是一个双向循环链表的实现,其在JavaDocs中说明既可以作为栈来使用也可以作为队列来使用。

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

用于保存链表的头指针

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

用于保存链表的大小

	private transient int size = 0;

默认构造函数,初始化链表的前后指针,构成初始的环路

	public LinkedList() {
		header.next = header.previous = header;
	}

另一个构造器,传入一个已有的集合,根据已有的集合构建列表,首先调用默认的构造器,然后添加已有集合中的所有元素,添加方法后续说明。

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

为了便于说明,提前来看一下一个内部类Entry,其中有三个属性,element用于存储数据,next是后指针,previous是前指针。另外一个有参的构造方法。

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

获得首个元素,实现很简单,就是返回头指针的后指针指向的元素的元素值。

	public E getFirst() {
		if (size == 0)
			throw new NoSuchElementException();
		return header.next.element;
	}

同理可得获得最后一个元素的代码实现。

	public E getLast() {
		if (size == 0)
			throw new NoSuchElementException();

		return header.previous.element;
	}

这是一个私有方法,不会被外部直接调用,目的是删除一个元素。删除的时候就是双向循环链表的删除,前后指针的移动,以及为了垃圾回收而写的前后指针置为null,最后返回被删除的元素。

	private E remove(Entry<E> e) {
		if (e == header)
			throw new NoSuchElementException();

		E result = e.element;
		e.previous.next = e.next;
		e.next.previous = e.previous;
		e.next = e.previous = null;
		e.element = null;
		size--;
		modCount++;
		return result;
	}

下面两段源码不解释。

	public E removeFirst() {
		return remove(header.next);
	}

	public E removeLast() {
		return remove(header.previous);
	}

实际中会被外部调用的删除方法,特殊在于对于null值的判断采用不同的分支。实际调用的依然是查找到位置后调用私有的remove方法。

	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 {
			for (Entry<E> e = header.next; e != header; e = e.next) {
				if (o.equals(e.element)) {
					remove(e);
					return true;
				}
			}
		}
		return false;
	}

判断元素的位置,和上面的方法类似,依然是对null值的特殊处理。

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

这里有一个非常有趣的私有方法,entry(index)用于获得某个索引的Entry对象。为什么说有趣呢?因为在判断完索引是否越界后进行了一步判断,就是索引位置在列表中的大体位置,是在前一半还是后一半,然后采用不同的遍历顺序,这样可以加快一倍的查找速度。

	private Entry<E> entry(int index) {
		if (index < 0 || index >= size)
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
		Entry<E> e = header;
		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;
	}

下面的公共方法就是调用了上面的私有方法,实现快速的对某个索引位置元素的删除。

	public E remove(int index) {
		return remove(entry(index));
	}

下面的设置方法也是调用上面的私有方法,先查找到元素,再进行元素值的替换覆盖。

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

那这样get方法也很好理解了吧。

	public E get(int index) {
		return entry(index).element;
	}

清空操作不能直接修改头指针的前后指针,这样指针虽然可以置空,但是其链表中的元素依然在内存中占有一定的位置,所以代码中的实现是优秀的,遍历链表将每个元素置为null。保证的后续gc的工作。

	public void clear() {
		Entry<E> e = header.next;
		while (e != header) {
			Entry<E> next = e.next;
			e.next = e.previous = null;
			e.element = null;
			e = next;
		}
		header.next = header.previous = header;
		size = 0;
		modCount++;
	}

下面是一个私有方法,从方法名中可以看到方法的作用是在e的元素之前添加entry元素。实际就是链表的插入操作,但是要保证head头指针的后指针指向最先插入的元素,head头指针的前指针指向最后插入的元素。

	private Entry<E> addBefore(E e, Entry<E> entry) {
		Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
		newEntry.previous.next = newEntry;
		newEntry.next.previous = newEntry;
		size++;
		modCount++;
		return newEntry;
	}

众望所归的add方法,调用上面的私有方法addBefore

	public boolean add(E e) {
		addBefore(e, header);
		return true;
	}

这个方法用于添加一个元素到列表的最前面,实际就是在头指针的后指针之前添加元素。

	public void addFirst(E e) {
		addBefore(e, header.next);
	}

下面的方法在列表末尾添加元素,其实和普通的add方法相同,但是没有返回值。

	public void addLast(E e) {
		addBefore(e, header);
	}

下面的方法用于在某个index后加入一个已有列表的集合,特殊在于index位置指针的处理,只是在最后一行代码中进行指针的修改指向。

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

		Entry<E> successor = (index == size ? header : entry(index));
		Entry<E> predecessor = successor.previous;
		for (int i = 0; i < numNew; i++) {
			Entry<E> e = new Entry<E>((E) a[i], successor, predecessor);
			predecessor.next = e;
			predecessor = e;
		}
		successor.previous = predecessor;

		size += numNew;
		return true;
	}

不带index参数的addAll方法就是在列表的最末尾插入元素,所以index就是大小size

	public boolean addAll(Collection<? extends E> c) {
		return addAll(size, c);
	}

下面是源码中为了实现队列功能而实现的方法,比较简单就不贴代码了。

 

下面是其内部自带的迭代器,这个迭代器也会抛出ConcurrentModificationException异常。其实现方法和其他的迭代器相同,依然是比对修改的次数。特殊在于他的构造函数是有参数的,需要传入一个index,就是你要迭代的范围。

	private class ListItr implements ListIterator<E> {
		ListItr(int index)
 

序列化和反序列化就不说了。

 

下面这个方法会返回一个反向的迭代器,是在jdk1.6时新增的方法。大家看看就好。

	public Iterator<E> descendingIterator() {
		return new DescendingIterator();
	}

	private class DescendingIterator implements Iterator {
		final ListItr itr = new ListItr(size());

		public boolean hasNext() {
			return itr.hasPrevious();
		}

		public E next() {
			return itr.previous();
		}

		public void remove() {
			itr.remove();
		}
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值