LinkedList源码分析

LinkedList源码分析

1.总体概览

LinkedList的继承体系:
在这里插入图片描述

2.属性分析

LinkedList总共有三个属性:

transient int size = 0;

transient Node<E> first;

transient Node<E> last;

  • size:即是LinkedList的大小
  • Node first:即是LinkedList的第一个节点
  • Node last:即是LinkedList的最后一个节点

这里看一下Node这个类。熟悉C或者C++的人对Node这个名词一定不陌生,和C/C++语言中的Node一样,LinkedList中的Node中,也包括两个Node,一个指向前一个Node,一个指向下一个Node,形成一个链表结构。

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

3.方法分析

  1. 构造一个空的LinkedList
public LinkedList() {
}
  1. 构造一个包含指定集合的LinkedList,并且是按照集合的迭代器顺序的
    主要调用public boolean addAll(int index, Collection<? extends E> c){}方法在指定的位置index开始添加元素
  • 首先对下标index进行检查,如果指定的位置不正确,抛出异常
  • 将指定集合转为数组,对数组的长队进行判断,如果为零则返回false,表示添加失败
  • 定义一个前节点pred,一个后节点succ
  • 如果下标index等于LinkedList的长度,代表是从链表末尾开始添加,后节点succ为空,前节点pred是链表最后一个节点;否则后节点succ是一个新的节点,前节点pred是后节点succ的前一个节点
  • 遍历数组,将数组中的每个元素构造成一个新节点newNode,newNode的前一个节点是pred,后一个节点是null。如果pred是空,则新节点newNode是链表第一个节点first,否则将新节点newNode添加到pred后面。每次循环结束,新节点newNode称为前节点pred,进行下一个的节点添加
  • 循环结束后,如果后节点succ为空,则前节点pred是尾节点last;否则将后节点succ添加到前节点pred末尾
  • 将数组长度增加到链表长度
public LinkedList(Collection<? extends E> c) {
	this();
	addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
	return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
	checkPositionIndex(index);

	Object[] a = c.toArray();
	int numNew = a.length;
	if (numNew == 0)
		return false;

	Node<E> pred, succ;
	if (index == size) {
		succ = null;
		pred = last;
	} else {
		succ = node(index);
		pred = succ.prev;
	}

	for (Object o : a) {
		@SuppressWarnings("unchecked") E e = (E) o;
		Node<E> newNode = new Node<>(pred, e, null);
		if (pred == null)
			first = newNode;
		else
			pred.next = newNode;
		pred = newNode;
	}

	if (succ == null) {
		last = pred;
	} else {
		pred.next = succ;
		succ.prev = pred;
	}

	size += numNew;
	modCount++;
	return true;
}
private void checkPositionIndex(int index) {
	if (!isPositionIndex(index))
		throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
	return index >= 0 && index <= size;
}

3.移除第一个节点
首先进行检查,如果第一个节点已经是空,则抛出异常。然后调用private E unlinkFirst(Node f){}方法移除第一个节点

  • 获取第一个节点first的值item和下一个节点next的引用
  • 将第一个节点first的值和下一个节点的应用置为空,进行垃圾回收
  • 将下一个节点next设置为链表的第一个节点
  • 如果节点next为空,代表这是链表最后一个节点,将链表尾节点last置为空;否则将新的首节点的前节点引用置为空,因为这是链表第一个节点,前面没有节点
  • 将链表长度减一,返回删除节点的值item
public E removeFirst() {
	final Node<E> f = first;
	if (f == null)
		throw new NoSuchElementException();
	return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
	// assert f == first && f != null;
	final E element = f.item;
	final Node<E> next = f.next;
	f.item = null;
	f.next = null; // help GC
	first = next;
	if (next == null)
		last = null;
	else
		next.prev = null;
	size--;
	modCount++;
	return element;
}
  1. 移除最后一个节点
    首先获取最后一个节点last进行判断,如果该节点已经为空则抛出异常,否则调用private E unlinkLast(Node l){}移除最后一个节点,思想和移除第一个节点大致相同
  • 获取最后一个节点last的值item和前一个节点pred的引用
  • 将最后一个节点last的值和前一个节点pred置为空,进行垃圾回收
  • 将前一个节点prev设置为链表的最后一个节点
  • 如果节点prev为空,代表这是链表最后一个节点,将链表首节点pred置为空;否则将新的尾节点的后节点引用置为空,因为这是链表最后一个节点,后面没有节点
  • 将链表长度减一,返回删除节点的值item
public E removeLast() {
	final Node<E> l = last;
	if (l == null)
		throw new NoSuchElementException();
	return unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
	// assert l == last && l != null;
	final E element = l.item;
	final Node<E> prev = l.prev;
	l.item = null;
	l.prev = null; // help GC
	last = prev;
	if (prev == null)
		first = null;
	else
		prev.next = null;
	size--;
	modCount++;
	return element;
}
  1. 在链表首增加一个节点
    调用private void linkFirst(E e){}方法在链表首增加一个节点
  • 取出链表本来的首节点first,赋值给f,构造一个包含指定元素的新节点newNode,新节点newNode的前节点引用为空,后节点的引用为f
  • 将新节点newNode赋值给first
  • 如果首节点f为空,代表原来链表为空,将新节点newNode同事赋值给尾节点last;否则将首节点f的前节点引用指向新节点newNode,将链表链在新节点newNode的后面,此时新节点newNode就成了首节点
  • 将链表长度加一
public void addFirst(E e) {
	linkFirst(e);
}
private void linkFirst(E e) {
	final Node<E> f = first;
	final Node<E> newNode = new Node<>(null, e, f);
	first = newNode;
	if (f == null)
		last = newNode;
	else
		f.prev = newNode;
	size++;
	modCount++;
}
  1. 在链表尾增加一个节点
    调用void linkLast(E e){}方法在链表尾增加一个节点,思路和在链表首增加一个节点大致相同
  • 取出链表本来的尾节点last,赋值给l,构造一个包含指定元素的新节点newNode,新节点newNode的前节点引用为l,后节点的引用为空
  • 将新节点newNode赋值给last
  • 如果尾节点l为空,代表原来链表为空,将新节点newNode同事赋值给首节点first;否则将尾节点l的后节点引用指向新节点newNode,将链表链在新节点newNode的前面,此时新节点newNode就成了尾节点
  • 将链表长度减一
public void addLast(E e) {
	linkLast(e);         
}
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++;
}
  1. 清空链表
    遍历整个链表,去除此次遍历的节点x,将x的下一个节点复制给next,将x的前节点引用,值,后节点引用都置为null,进行垃圾回收,然后再讲next赋值给x,进行下一次的循环。最后将首尾节点都置为空,将链表长度置为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++;
}
  1. 判断链表是否包含某个元素
    判断链表是否包含某个元素,也就是计算这个元素在链表中的下标,如果返回为-1,则代表链表中不含这个元素
  • 如果元素为空,遍历链表中的空值
  • 如果元素不为空,遍历链表中符合条件的值,找不到返回-1
  • 如果返回-1,代表链表中不包含这个元素,返回false
public boolean contains(Object o) {
	return indexOf(o) != -1;
}
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 {
		for (Node<E> x = first; x != null; x = x.next) {
			if (o.equals(x.item))
				return index;
			index++;
		}
	}
	return -1;
}
  1. 返回链表长度
    返回链表长度,即size的值
public int size() {
	return size;
} 
  1. 增加一个元素
    在链表尾用void linkLast(E e){}方法增加一个节点
public boolean add(E e) {
	linkLast(e);
	return true;
}
  1. 移除一个包含指定元素的节点
    首先对改元素进行空值判断,遍历到改节点的位置,调用E unlink(Node x){}方法从链表中剔除该节点
  • 依次取出改节点的值item、前节点的引用prev、后节点的引用next
  • 如果前节点prev引用为空,那这就是首节点first,将后节点next设置为新的首节点first;否则将后节点next的引用赋值给前节点prev的后节点引用,即将后面的节点直接链接在前面节点后面,忽略这个节点,将prev置为空
  • 如果后节点next引用为空,那这就是尾节点last,将前节点的引用赋值给尾节点last;否则将前节点prev的引用赋值给后面一个节点next的前节点引用,将next置为空,忽略这个节点
  • 最后将值item设置为空,将链表长度size减一
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 {
		for (Node<E> x = first; x != null; x = x.next) {
			if (o.equals(x.item)) {
				unlink(x);
				return true;
			}
		}
	}
	return false;
}
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;
	size--;
	modCount++;
	return element;
}
  1. 返回指定下标index的节点值
    先进行下标检查,然后调用Node node(int index){}获取节点的值
  • 这里看出取值时不是从头开始遍历的,而是对链表长度一分为二,分两段遍历。前半段顺序遍历,后半段逆序遍历。因为每个节点都有前节点prev的引用,所以可以逆序遍历
public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}
Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        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;
    }
}
  1. 替换指定下标index的值
    首先对下标index进行检查,然后调用Node node(int index){}取到下标index对应的节点,将节点的值item替换为element,返回节点之前保存的值
public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}
  1. 在指定下标index处增加一个值之元素element的节点
    如果指定下标刚好等于链表的长度,则调用void linkLast(E e){}方法在链表尾增加一个节点;否则调用void linkBefore(E e, Node succ){}方法在指定下标的节点前增加一个节点
  • 先取出指定下标的节点succ的前节点pred,构造一个前节点为pred,值为element,后节点为succ的新节点newNode
  • 将指定下标的节点succ的前节点引用设置为newNode,代表将后面所有的节点都链接在新的节点newNode的后面
  • 如果前节点pred为空,代表增加的位置在链表首,将新节点newNode设置为首节点first;否则将新节点newNode赋值给指定下标的节点succ的前节点pred的后节点引用,将以新节点为首的链表链接在后面
  • 将链表长度size加一
public void add(int index, E element) {
    checkPositionIndex(index);

    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
	// assert succ != null;
	final Node<E> pred = succ.prev;
	final Node<E> newNode = new Node<>(pred, e, succ);
	succ.prev = newNode;
	if (pred == null)
		first = newNode;
	else
		pred.next = newNode;
	size++;
	modCount++;
}
  1. 移除指定下标的节点
    先进行下标的判断,然后调用E unlink(Node x){}方法从链表中剔除该节点(此方法已经前面描述过)
public E remove(int index) {
	checkElementIndex(index);
	return unlink(node(index));
}
  1. 找出链表中最后一个值为Object的下标
    逆序遍历链表,找出符合条件的下标,同样分空值和非空值进行判断
public int lastIndexOf(Object o) {
	int index = size;
	if (o == null) {
		for (Node<E> x = last; x != null; x = x.prev) {
			index--;
			if (x.item == null)
				return index;
		}
	} else {
		for (Node<E> x = last; x != null; x = x.prev) {
			index--;
			if (o.equals(x.item))
				return index;
		}
	}
	return -1;
}
  1. 取出链表首节点的值,但是并不删除
    取出第一个节点的值,如果是空则返回空,否则返回节点的值
public E peek() {
	final Node<E> f = first;
	return (f == null) ? null : f.item;
}
  1. 取出链表第一个元素的值,但是并不删除
public E element() {
	return getFirst();
}
public E getFirst() {
	final Node<E> f = first;
	if (f == null)
		throw new NoSuchElementException();
	return f.item;
}
  1. 弹出首节点
    这个方法使得链表像队列一样,弹出并删除第一个节点的值
public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
  1. 删除链表的一个节点
    调用public E removeFirst(){}方法,实际从链表首开始删除节点
public E remove() {
	return removeFirst();
}
  1. 增加一个节点
    实际调用的public boolean add(E e){}方法在链表末尾增加一个节点
public boolean offer(E e) {
	return add(e);
}
  1. 在链表首增加一个节点
    实际调用的是public void addFirst(E e){}方法
public boolean offerFirst(E e) {
	addFirst(e);
	return true;
}
  1. 在链表尾增加一个节点
    实际调用的是public void addLast(E e){}方法
public boolean offerLast(E e) {
	addLast(e);
	return true;
}
  1. 取出首节点的值
public E peekFirst() {
	final Node<E> f = first;
	return (f == null) ? null : f.item;
 }
  1. 取出尾节点的值
public E peekLast() {
	final Node<E> l = last;
	return (l == null) ? null : l.item;
}
  1. 弹出首节点的值
    取出首节点的值并删除
public E pollFirst() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }
  1. 弹出尾节点的值
public E pollLast() {
	final Node<E> l = last;
	return (l == null) ? null : unlinkLast(l);
}
  1. 在链表首压入一个节点
    实际调用的是public void addFirst(E e){}方法
public void push(E e) {
	addFirst(e);
}
  1. 弹出链表首的节点
    实际调用的是public E removeFirst(){}方法
public E pop() {
	return removeFirst();
}
  1. 删除第一个出现的包含某个值的节点
    实际调用的是public boolean remove(Object o){}方法
public boolean removeFirstOccurrence(Object o) {
	return remove(o);
}
  1. 删除最后一次出现包含某个值的节点
    逆序遍历链表,找出符合条件的下标,同样分空值和非空值进行判断,找到则调用E unlink(Node x){}方法删除
public boolean removeLastOccurrence(Object o) {
	if (o == null) {
		for (Node<E> x = last; x != null; x = x.prev) {
			if (x.item == null) {
				unlink(x);
				return true;
			}
		}
	} else {
		for (Node<E> x = last; x != null; x = x.prev) {
			if (o.equals(x.item)) {
				unlink(x);
				return true;
			}
		}
	}
	return false;
}
  1. 链表转数组
    这是一个比较简单的方法,构造以一个数组,一次将链表中节点的值加入数组
public Object[] toArray() {
	Object[] result = new Object[size];
	int i = 0;
	for (Node<E> x = first; x != null; x = x.next)
		result[i++] = x.item;
	return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值