`

LinkedList源码分析

阅读更多
LinkedList适用于添加、删除比较频繁,随机访问不多的场合。
LinkedList扩展了AbstractSequentialList抽象类(提供了部分List接口的实现),实现了List,Deque,Cloneable,java.io.Serializable接口。

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


LinkedList提供了双向链表的实现,每个元素都有两个指针,一个指向前一个元素,另一个指向后一个元素:
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;
	}
}


1. 构造方法

   head元素不代表任何具体内容。刚初始化的时候,LinkedList为空,header与它的前一个和后一个元素相同,都是它本身:

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


2. add, addBefore方法

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

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

a. 首先实例化这个节点,它的下个节点是entry,前个节点是entry.previous
b. 然后把前个节点的下个节点指针指向新节点,后个节点的前个节点指针指向新节点
c. 大小加1,修改次数加1。修改次数用在侦测并发修改方面。

3. remove(Entry<E> e)方法

remove(Object o)方法包含两个部分:首先搜索到该元素(对o为null的时候做了优化),然后调用remove(E e)。

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

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

a. header是不可以被删除的
b. 先把需要删除的节点的值保存,用于返回值
c. 把前后节点直接关联起来
d. 该节点清空
e. 大小减1,修改次数加1

4. addAll方法
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;
}

a. 找到index位置的节点和前节点。
b. 在它们之间插入一组节点,设置好前后关系。
c. 链表的长度增加。

5. getFirst, getLast方法
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;
}

获取头尾节点的速度会很快,因为离header很近。

6. contains和indexOf方法
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;
}

如果o为null,单独去比较(e.element == null),提高性能。

7. clear方法
public void clear() {
	Entry<E> e = header.next;
	while (e != header) {
		Entry<E> next = e.next; // record the next entry
		e.next = e.previous = null;
		e.element = null;
		e = next; // iterate through
	}
	header.next = header.previous = header;
	size = 0;
	modCount++;
}

a. 遍历所有的节点,清空它们的前后引用和值。
b. header节点的前后节点指向它本身。
c. 长度置为0,修改次数自增。

8. 索引访问
很明显,索引访问需要通过链表结构,速度上没有ArrayList的索引访问快。
    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;
    }

    // 拿到index位置上的元素,赋新的值,返回旧的值
    public E set(int index, E element) {
        Entry<E> e = entry(index);
        E oldVal = e.element;
        e.element = element;
        return oldVal;
    }

    // 判断添加的位置:如果在末尾,就是在header前插入;否则在index元素前插入
    public void add(int index, E element) {
        addBefore(element, (index==size ? header : entry(index)));
    }

    // 删除index位置的元素
    public E remove(int index) {
        return remove(entry(index));
    }

    // 从后向前搜索,返回第一个符合的元素的位置
    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;
    }


9. 队列操作
    // 获取第一个元素,如果队列为空,返回null
    public E peek() {
        if (size==0)
            return null;
        return getFirst();
    }

    // 获取第一个元素,如果队列为空,抛NoSuchElementException异常
    public E element() {
        return getFirst();
    }

    // 删除并返回第一个元素,如果队列为空,返回null
    public E poll() {
        if (size==0)
            return null;
        return removeFirst();
    }

    // 删除并返回第一个元素,如果队列为空,抛NoSuchElementException异常
    public E remove() {
        return removeFirst();
    }

    // 在队列尾部添加一个元素,LinkedList没有大小限制,一般不会返回false
    public boolean offer(E e) {
        return add(e);
    }


10. 双向队列操作
    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

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

    public E peekFirst() {
        if (size==0)
            return null;
        return getFirst();
    }

    public E peekLast() {
        if (size==0)
            return null;
        return getLast();
    }

    public E pollFirst() {
        if (size==0)
            return null;
        return removeFirst();
    }

    public E pollLast() {
        if (size==0)
            return null;
        return removeLast();
    }

    public void push(E e) {
        addFirst(e);
    }

    public E pop() {
        return removeFirst();
    }

    public boolean removeFirstOccurrence(Object o) {
        return remove(o);
    }

    public boolean removeLastOccurrence(Object o) {
        if (o==null) {
            for (Entry<E> e = header.previous; e != header; e = e.previous) {
                if (e.element==null) {
                    remove(e);
                    return true;
                }
            }
        } else {
            for (Entry<E> e = header.previous; e != header; e = e.previous) {
                if (o.equals(e.element)) {
                    remove(e);
                    return true;
                }
            }
        }
        return false;
    }
分享到:
评论

相关推荐

    LinkedList源码分析_016.pdf

    《LinkedList源码解析》 LinkedList是Java集合框架中的一员,它是AbstractSequentialList的子类,同时也实现了List、Deque和Cloneable、Serializable接口。LinkedList作为双向链表,支持快速的添加、删除元素,尤其...

    Java集合系列之LinkedList源码分析

    Java集合系列之LinkedList源码分析 概述: 本文详细介绍了Java集合系列之LinkedList的源码分析,主要介绍了LinkedList的底层实现、成员变量、构造器、增删改查方法等。LinkedList是一种基于双向链表的数据结构,...

    Java LinkedList源码分析

    简介  LinkedList 是一个常用的集合类,用于顺序存储元素。 LinkedList 经常和 ArrayList 一起被提及。...本文分析 LinkedList 的具体实现。  继承关系  public class LinkedList  extends AbstractS

    LinkedList源码学习分析

    《LinkedList源码学习分析》 LinkedList作为Java集合框架中的一员,是基于链表数据结构实现的线程不安全容器。本文将深入探讨LinkedList的实现原理、核心方法的代码实现,并对比ArrayList,理解其特性和使用场景。 ...

    Java 集合之LinkedList源码分析

    LinkedList是通过节点的连接实现链表的数据结构,向linkedList中插入或删除元素的速度是特别快,而随机访问的速度相对较慢,这个是由于链表本身的性质造成的,在链表中,每个节点都包含了前一个节点的引用,后一个...

    【死磕Java集合】-集合源码分析.pdf

    一、LinkedList源码分析 LinkedList是一种以双向链表实现的List,它除了作为List使用,还可以作为队列或者栈来使用。它实现了List、Queue和Deque接口,使其具有多种使用场景。 LinkedList的继承体系中,它继承了...

    LinkedList 部分源码

    ### LinkedList部分源码解析 #### 一、简介 `LinkedList`是Java集合框架的一个重要组成部分,它基于双向链表实现,既支持`List`接口也实现了`Deque`接口,因此可以作为列表、栈或者队列使用。双向链表的每个节点...

    JDK1.6中Arraylist,Vector,LinkedList源码

    源码分析时,可以关注以下几个关键点: 1. 容量管理:观察ArrayList和Vector如何进行扩容,了解它们扩容的策略和成本。 2. 插入和删除:比较ArrayList、Vector和LinkedList在插入和删除元素时的代码实现,分析时间...

    计算机后端-Java-Java核心基础-第24章 集合01 15. LinkedList的源码分析.avi

    计算机后端-Java-Java核心基础-第24章 集合01 15. LinkedList的源码分析.avi

    Java基于JDK 1.8的LinkedList源码详析

    "Java基于JDK 1.8的LinkedList源码详析" LinkedList是Java中一个非常重要的数据结构,基于双向链表实现,适用于...通过对LinkedList源码的分析,我们可以更好地了解其内部实现机制和特点,并更好地应用于实际开发中。

    ArrayList-LinkedList-源码.rar

    《ArrayList与LinkedList源码解析》 在Java编程中,ArrayList和LinkedList是两种常见的动态数组,它们都是Java集合框架的一部分,提供了对元素存储和操作的功能。本篇将深入探讨ArrayList和LinkedList的内部实现...

    Map+List+ArrayList+LinkedList Java源码

    **源码分析** 深入研究这些类的源码,可以帮助我们理解它们是如何在内存中组织数据以及如何执行各种操作的。例如,`HashMap`的哈希函数如何计算元素的桶位置,`ArrayList`如何调整其容量,以及`LinkedList`如何通过...

    Android+上百实例源码分析以及开源分析+集合打包3

    在Android开发领域,深入理解和应用源码分析以及开源库的运用是提升技能的关键步骤。"Android+上百实例源码分析以及开源分析+集合打包3"这个资源提供了丰富的学习材料,涵盖了多个方面,旨在帮助开发者更好地理解和...

    java LinkedList源码详解及实例

    3. **源码分析**: - `getFirst()`和`getLast()`通过直接访问`first`和`last`属性来获取元素,这两个属性分别保存链表的第一个和最后一个节点。 - `removeFirst()`和`removeLast()`通过`unlinkFirst(Node&lt;E&gt; f)`和...

    Android_上百实例源码分析以及开源分析_集合打包4

    在Android开发领域,源码分析和开源项目的理解是提升技能的关键步骤。"Android_上百实例源码分析以及开源分析_集合打包4"这个资源显然旨在帮助开发者深入掌握Android应用程序的内部工作原理,以及如何利用开源库优化...

    分析Java中ArrayList与LinkedList列表结构的

    **LinkedList源码分析** LinkedList是基于双向链表实现的,每个元素都是一个Node对象,包含前一个节点、后一个节点和存储的值。相比于ArrayList,LinkedList在插入和删除操作上更高效,因为不需要移动大量元素,但...

    JUC并发编程与源码分析视频课.zip

    《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...

    Java中的ArrayList的底层源码解读、LinkedList、Vector的区别介绍

    能学到什么:ArrayList的源码分析,自动扩容和自动缩容的源码分析,相关参数的深度解析,从是什么,为什么,怎么做三个角度进行讲解,用通俗易懂的白话进行介绍,LinkedList和Vector以及ArrayList的区别以及使用场景...

    源码解析jdk7.0集合:LinkedList的底层实现原理.pdf

    在具体源码分析中,Node节点内部类的实现是LinkedList的基础。Node内部类的定义方式是: ```java private static class Node&lt;E&gt; { E item; Node&lt;E&gt; next; Node&lt;E&gt; prev; Node(Node&lt;E&gt; prev, E element, Node...

Global site tag (gtag.js) - Google Analytics