常用集合※【LinkedList】

LinkedList是基于双向循环链表实现的,所以要对linkedList有全面的了解和认识,必须知道链表是如何实现的。

链表

什么是链表呢?

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。如图:

简单代码实现方式:

	public class Node {

		String data;//存放节点数据本身
		Node next;//存放下一个节点的引用
		public Node(String data){
			this.data=data;
		}
	}
	public static void main(String[] args) {

		Node node1=new Node("node1");
		Node node2=new Node("node2");
		Node node3=new Node("node3");

		node1.next=node2;
		node2.next=node3;
		System.out.println(node1.next.next.data);
	}

输出结果是node3.

下面在介绍一下双向循环链表,图:

简单代码实现方式:

	public class Node2 {

		Node2 previous;
		String data;
		Node2 next;

		public Node2(String data){
			this.data=data;
		}
	}

	public static void main(String[] args) {

			Node2 node1=new Node2("node1");

			Node2 node2=new Node2("node2");

			Node2 node3=new Node2("node3");

			node1.next=node2;
			node2.previous=node1;

			node2.next=node3;
			node3.previous=node2;

			node3.next=node1;
			node1.previous=node3;

			System.out.println(node1.next.next.data);
			System.out.println(node1.previous.previous.data);
		}

输出结果:node3,node2

通过对链表的学习,明白了链表的实现方式。所以对LinkedList的基本实现原理应该有大致了解了吧。

LinkeList集合结构

public class LinkedList<E>
   extends AbstractSequentialList<E>
      implements List<E>, Deque<E>, Cloneable, Serializable<span style="color:#00cccc;">
</span>

LinkedList继承了AbstractSequentialList,并实现了List接口,即具有了添加,删除,修改,遍历等功能。

LinkedList是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。

LinkedList实现 Deque接口,即能将LinkedList当作双端队列使用。

LinkedList实现了Cloneable接口,即覆盖了函数clone(),能克隆。

LinkedList实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。

私有属性

在LinkedList底层实现类中,定义了2个私有的属性,如下:

private transient Entry<E> header = new Entry<E>(null, null, null);
  private transient int size = 0;//size是双向链表中节点实例的个数。

Header在定义时new了一个Entry对象,那Entry对象里有是什么呢?

  我们通过下面的源码可以看出Entry对象中包含成员变量:previous, next, element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。

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

header 是LinkedList的关键,它在链表中没有实际数据意义,是链表的标示(通俗一点就是链表的第一个无意义的元素),而且被修饰为transient,标示着他不会被序列化。header也可以当做队列末尾的元素,因为是双向列表,所以header.next末尾元素后边的元素就成了队首元素,header.previous就是队尾元素了,看一下它的添加方法:

public void addFirst(E paramE) {
    addBefore(paramE, this.header.next);//队首
}
public void addLast(E paramE) {
    addBefore(paramE, this.header);//队尾  

}  

构造方法

public LinkedList() {
     header.next = header.previous = header;
 }
 public LinkedList(Collection<? extends E> c) {
     this();
   addAll(c);
 }

  第一个构造方法不接受参数,将header实例的previous和next全部指向header实例(注意,这个是一个双向循环链表,如果不是循环链表,空链表的情况应该是header节点的前一节点和后一节点均为null),这样整个链表其实就只有header一个节点,用于表示一个空的链表。

执行完构造函数后,header实例自身形成一个闭环,如下图所示:

第二个构造方法接收一个Collection参数c,调用第一个构造方法构造一个空的链表,之后通过addAll将c中的元素全部添加到链表中。

代码如下:

public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}
// index参数指定collection中插入的第一个元素的位置
public boolean addAll(int index, Collection<? extends E> c) {
    // 插入位置超过了链表的长度或小于0,报IndexOutOfBoundsException异常
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+size);
    Object[] a = c.toArray();
   int numNew = a.length;
   // 若需要插入的节点个数为0则返回false,表示没有插入元素
    if (numNew==0)
        return false;
    modCount++;//否则,插入对象,链表修改次数加1
    // 保存index处的节点。插入位置如果是size,则在头结点前面插入,否则在获取index处的节点插入
    Entry<E> successor = (index==size ? header : entry(index));
    // 获取前一个节点,插入时需要修改这个节点的next引用
    Entry<E> predecessor = successor.previous;
    // 按顺序将a数组中的第一个元素插入到index处,将之后的元素插在这个元素后面
    for (int i=0; i<numNew; i++) {
        // 结合Entry的构造方法,这条语句是插入操作,相当于C语言中链表中插入节点并修改指针
        Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
        // 插入节点后将前一节点的next指向当前节点,相当于修改前一节点的next指针
        predecessor.next = e;
        // 相当于C语言中成功插入元素后将指针向后移动一个位置以实现循环的功能
        predecessor = e;
  }
    // 插入元素前index处的元素链接到插入的Collection的最后一个节点
    successor.previous = predecessor;
    // 修改size
    size += numNew;
    return true;
}

加入第一个节点后LinkedList示意图

添加第二个元素后:

总结

LinkedList是通过节点直接彼此连接来实现的。每一个节点都包含前一个节点的引用,后一个节点的引用和节点存储的值。当一个新节点插入时,只需要修改其中保持先后关系的节点的引用即可,当删除记录时也一样。

所以说LinkedList适用于查询需求不大,但是增加和删除特别频繁的功能。

但是LinkedList不能随即访问虽然存在get()方法,但是这个方法是通过遍历接点来定位的所以速度慢。

时间: 2024-08-09 22:03:14

常用集合※【LinkedList】的相关文章

比较Java中几个常用集合添加元素的效率

初始化需要进行比较的集合,统一增加10万个元素,获取整个过程的执行时间. 1.List集合增加元素 1 private static void testList() { 2 3 List<Integer> list = new ArrayList<Integer>(); 4 5 long startTime = System.currentTimeMillis(); // 获取开始时间 6 for (int i = 0; i < 100000; i++) { 7 8 list

【总结】Java常用集合接口与集合类

目录 常见集合接口概述 Collection<E> Map<K,V> Collection接口 Map接口 补充内容 ? 常见集合接口概述 Java中包含许多集合接口.其中比较常见的主要是Collection接口和Map接口: 1.1 Collection<E> 由单元素组成的集合.其比较常见的直接子接口是List.Set和Queue接口. ? ? ? ? 表1.1 Collection<e>接口常用方法 编号 方法原型 解释 备注 1 boolean?ad

JAVA集合框架中的常用集合及其特点、适用场景、实现原理简介

JJDK提供了大量优秀的集合实现供开发者使用,合格的程序员必须要能够通过功能场景和性能需求选用最合适的集合,这就要求开发者必须熟悉Java的常用集合类.本文将就Java Collections Framework中常用的集合及其特点.适用场景.实现原理进行介绍,供学习者参考.当然,要真正深入理解Java的集合实现,还是要推荐去阅读JDK的源码. Java提供的众多集合类由两大接口衍生而来:Collection接口和Map接口 Collection接口 Collection接口定义了一个包含一批对

.NET基础 (09)常用集合和泛型

常用集合和泛型1 int[]是引用类型还是值类型2 数组之间如何进行转换3 解释泛型的基本原理4 什么是泛型的主要约束和次要约束 常用集合和泛型1 int[]是引用类型还是值类型 数组类型是一族类型,它们都继承自System.Array,而System.Array又继承自System.Object.所有数组的类型都是引用类型. 引用类型的数组和值类型的数组的内存分配: 2 数组之间如何进行转换 数组类型在符合条件的情况下可以进行隐式地转换,条件包括:数组维数必须相同:目标项目类型和源项目类型必须

Java入门系列之集合LinkedList入门(八)

前言 前面两节内容我们详细介绍了ArrayList,一是手写实现ArrayList数据结构,而是通过分析ArrayList源码看看内置实现,关于集合内容一如既往,本节课我们继续学习集合LinkedList,我们首先入门LinkedList数据结构,然后再去看看LinkedList源码是如何实现的,我们开始吧. LinkedList入门 LinkedList内置是通过双链表数据结构来存储数据,和ArrayList不同的是,ArrayList属于真正意义物理意义上的线性结构,而LinkedList也

C#常用集合

数组的缺点:长度固定.因此引入集合的使用. 注:泛型集合更安全,性能更高. 常用集合 对应泛型 ①动态数组ArrayList    List<T> 常用方法属性:Add  Clear  Contains  IndexOf  Insert  Remove  Sort ②哈希表Hashtable   Dictionary<TKey,TValue> 常用方法属性:Add  Clear  ContainsKey  ContainsValue  Remove ③排序列表SortedList 

c++STL之常用集合算法

set_intersection:求两个容器的交集 set_union:求两个集合的并集 set_difference:求两个集合的差集 1.set_intersection #include<iostream> using namespace std; #include <vector> #include <algorithm> //常用集合算法 set_intersection void myPrint(int val) { cout << val &l

C#常用集合的使用

大多数集合都在System.Collections,System.Collections.Generic两个命名空间.其中System.Collections.Generic专门用于泛型集合. 针对特定类型的集合类型位于System.Collections.Specialized;命名空间: 线程安全的集合类位于System.Collections.Concurrent;命名空间. 下面是集合和列表实现的接口如下: 一.列表 [csharp] view plaincopy [Serializab

Java 常用集合操作

List接口是Collection的子接口,用于定义线性表结构,其中ArrayList可以理解为一个动态数组,而LinkedList可以理解为一个链表 常用操作: 插入和删除操作: void add(int index,E element): 将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动. E remove(int index): 删除给定位置的元素,并将被删除的元素返回. get和set方法: List除了继承Collection定义的方法外,还根据其线性表的数据结构定义了一系列