链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址 链表可分为单向链表和双向链表。 一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接 Java LinkedList - 图1 一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接 Java LinkedList - 图2 Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。 与 ArrayList 相比,LinkedList 的增加和删除对操作效率更高,而查找和修改的操作效率较低

ArrayList与LinkedList的使用挑选

以下情况使用 ArrayList :

  • 频繁访问列表中的某一个元素
  • 只需要在列表末尾进行添加和删除元素操作

以下情况使用 LinkedList :

  • 你需要通过循环迭代来访问列表中的某些元素
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作

LinkedList链表储存结构与特点

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成
链表特点:

  • 查询慢 链表中地址不是连续的,每次查询元素都必须从头开始查询
  • 增删快 链表结构增删一个元素,对链表的整体结构没有什么影响,所以增删快

每个结点包括两个部分:

  • 存储数据元素的数据域
  • 存储下一个结点地址的指针域

我们常说的链表结构有单向链表与双向链表

  • 单向链表 只有一条链子,不能保证元素的顺序(存储元素和取出元素顺序有可能不一致)

image.png

  • 双向链表 有两条链子,有一条是专门记录元素的顺序,是一个有序的集合

image.png
image.png

LinkedList继承与接口关系

Java LinkedList - 图6

  • LinkedList 继承了 AbstractSequentialList 类。
  • LinkedList 实现了 Queue 接口,可作为队列使用
  • LinkedList 实现了 List 接口,可进行列表的相关操作
  • LinkedList 实现了 Deque 接口,可作为队列使用
  • LinkedList 实现了 Cloneable 接口,可实现克隆
  • LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输

LinkedList 类位于 java.util 包中,使用前需要引入它,语法格式如下:

  1. // 引入 LinkedList 类
  2. import java.util.LinkedList;
  3. LinkedList<E> list = new LinkedList<E>(); // 普通创建方法
  4. 或者
  5. LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表

常用方法

方法 描述
public boolean add(E e) 链表末尾添加元素,返回是否成功,成功为 true,失败为 false
public void add(int index, E element) 向指定位置插入元素
public boolean addAll(Collection c) 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false
public boolean addAll(int index, Collection c) 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false
public void addFirst(E e) 元素添加到头部
public void addLast(E e) 元素添加到尾部
public boolean offer(E e) 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false
public boolean offerFirst(E e) 头部插入元素,返回是否成功,成功为 true,失败为 false
public boolean offerLast(E e) 尾部插入元素,返回是否成功,成功为 true,失败为 false
public void clear() 清空链表
public E removeFirst() 删除并返回第一个元素
public E removeLast() 删除并返回最后一个元素
public boolean remove(Object o) 删除某一元素,返回是否成功,成功为 true,失败为 false
public E remove(int index) 删除指定位置的元素
public E poll() 删除并返回第一个元素
public E remove() 删除并返回第一个元素
public boolean contains(Object o) 判断是否含有某一元素
public E get(int index) 返回指定位置的元素
public E getFirst() 返回第一个元素
public E getLast() 返回最后一个元素
public int indexOf(Object o) 查找指定元素从前往后第一次出现的索引
public int lastIndexOf(Object o) 查找指定元素最后一次出现的索引
public E peek() 返回第一个元素
public E element() 返回第一个元素
public E peekFirst() 返回头部元素
public E peekLast() 返回尾部元素
public E set(int index, E element) 设置指定位置的元素
public Object clone() 克隆该列表
public Iterator descendingIterator() 返回倒序迭代器
public int size() 返回链表元素个数
public ListIterator listIterator(int index) 返回从指定位置开始到末尾的迭代器
public Object[] toArray() 返回一个由链表元素组成的数组
public T[] toArray(T[] a) 返回一个由链表元素转换类型而成的数组

更多 API 方法可以查看:https://www.runoob.com/manual/jdk11api/java.base/java/util/LinkedList.html

LinkedList实例

创建一个简单的链表实例

  1. import java.util.LinkedList;
  2. public class TestLinkedList {
  3. public static void main(final String[] args) {
  4. final LinkedList<String> sites = new LinkedList<String>();
  5. sites.add("Google");
  6. sites.add("Apple");
  7. sites.add("MicroSoft");
  8. System.out.println(sites);
  9. }
  10. }
  11. // [Google,Apple,MicroSoft]

更多的情况下我们使用 ArrayList 访问列表中的随机元素更加高效,但以下几种情况 LinkedList 提供了更高效的方法

在列表开头添加元素:

import java.util.LinkedList;

public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList<String> sites = new LinkedList<String>();
        sites.add("Google");
        sites.add("Apple");
        sites.add("MicroSoft");

        sites.addFirst("Wiki");  // 使用 addFirst() 在头部添加元素
        System.out.println(sites); // [Wiki, Google, Apple, MicroSoft]
    }
}


在列表结尾添加元素

import java.util.LinkedList;

public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList<String> sites = new LinkedList<String>();
        sites.add("Google");
        sites.add("Apple");
        sites.add("MicroSoft");

        sites.addLast("Wiki");  // 使用 addLast() 在尾部添加元素
        System.out.println(sites); // [Google, Apple, MicroSoft, Wiki]
    }
}


在列表开头移除元素

import java.util.LinkedList;

public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList<String> sites = new LinkedList<String>();
        sites.add("Google");
        sites.add("Apple");
        sites.add("MicroSoft");

        sites.removeFirst(); // 使用 removeFirst() 移除头部元素
        System.out.println(sites); // [Apple, MicroSoft]
    }
}


在列表结尾移除元素

import java.util.LinkedList;

public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList<String> sites = new LinkedList<String>();
        sites.add("Google");
        sites.add("Apple");
        sites.add("MicroSoft");

        sites.removeLast(); // 使用 removeLast() 移除尾部元素
        System.out.println(sites); // [Google, Apple]
    }
}

获取列表开头的元素

import java.util.LinkedList;

public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList<String> sites = new LinkedList<String>();
        sites.add("Google");
        sites.add("Apple");
        sites.add("MicroSoft");

        // 使用 getFirst() 获取头部元素 
        System.out.println(sites.getFirst()); //Google
    }
}

获取列表结尾的元素:

import java.util.LinkedList;

public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList<String> sites = new LinkedList<String>();
        sites.add("Google");
        sites.add("Apple");
        sites.add("MicroSoft");

        // 使用 getLast() 获取尾部元素
        System.out.println(sites.getLast()); // MicroSoft
    }
}

迭代元素

我们可以使用 for 配合 size() 方法来迭代列表中的元素:

import java.util.LinkedList;

public class TestLinkedList {
    public static void main(String[] args) {
        LinkedList<String> sites = new LinkedList<String>();
        sites.add("Google");
        sites.add("Apple");
        sites.add("MicroSoft");

        for (int size = sites.size(), i = 0; i < size; i++) {
            System.out.println(sites.get(i));
        }

        for (String i : sites) {
            System.out.println(i);
        }
    }
}

/* 
Google
Apple
MicroSoft 
*/

拓展:自己实现一个链表

  1. 先建立链表结点以及整个链表的实体类;这里有两个实体类:
    • LinkNode是结点,包括结点的数据域和指针域;
    • LinkList是整个链表,包括头结点以及链表元素个数; ```jsx package com.java.model;

public class LinkNode { // 链表结点的数据域 public Object data; // 链表结点的指针域 public LinkNode next;

public LinkNode() { super(); // TODO Auto-generated constructor stub }

// 构造方法 public LinkNode(Object data, LinkNode next) { super(); this.data = data; this.next = next; }

}

```jsx
package com.java.model;

public class LinkList {
   //链表的头结点
   public LinkNode head;
   //链表的元素个数
   public int size;

   public LinkList() {
       super();
       // TODO Auto-generated constructor stub
   }

   ///构造方法
   public LinkList(LinkNode head, int size) {
       super();
       this.head = head;
       this.size = size;
   }
}
  1. 再写链表方法类LinkListDao; ```jsx package com.java.dao;

import com.java.model.LinkList; import com.java.model.LinkNode;

public class LinkListDao { // 初始化链表 public LinkList Init_LinkList() { // 设置头结点的指针域和数据域 LinkNode node = new LinkNode(0, null); LinkList list = new LinkList(node, 0); return list; }

// 指定位置插入 public void Insert_LinkList(LinkList list, int pos, Object data) { // 判断list是否有效 if (list == null) { return; } // 判断data是否有效 if (data == null) { return; } // 判断位置pos是否有效 if (pos < 0 || pos > list.size) { // 在链表的尾部插入 pos = list.size; }

// 第一步,创建新的结点,也就是待插入的结点
LinkNode newNode = new LinkNode(data, null);
// 第二步,找到待插入结点前面一个结点pCurrent,并使其等于list的头结点
LinkNode pCurrent = list.head;
for (int i = 0; i < pos; i++) {
  pCurrent = pCurrent.next;
}
// 第三步,新结点入链表,进行插入操作
newNode.next = pCurrent.next;
pCurrent.next = newNode;
// 第四步,链表的size要加1
list.size++;

}

// 删除指定位置的值 public void RemoveByPos_LinkList(LinkList list, int pos) { if (list == null) { return; } if (pos < 0 || pos >= list.size) { return; } // 第一步,找到待删除结点的前面一个结点pCurrent LinkNode pCurrent = list.head; for (int i = 0; i < pos; i++) { pCurrent = pCurrent.next; } // 第二步,进行删除操作 pCurrent.next = pCurrent.next.next; // 第三步,链表的size要减1 list.size—; }

// 获得链表的长度 public int Size_LinkList(LinkList list) { return list.size; }

// 查找指定元素的位置 public void Find_LinkList(LinkList list, Object data) { // 注意这里要从头结点的下一个结点开始,因为头结点不存放数据信息 LinkNode pCurrent = list.head.next; for (int i = 0; i < list.size; i++) { if (pCurrent.data == data) { System.out.print(i + “,”); } pCurrent = pCurrent.next; } }

// 返回第一个结点元素的值 public Object Front_LinkList(LinkList list) { return list.head.next.data; }

// 打印链表结点 public void Print_LinkList(LinkList list) { if (list == null) { return; } LinkNode pCurrent = list.head.next; for (int i = 0; i < list.size; i++) { System.out.print(pCurrent.data + “,”); pCurrent = pCurrent.next; } }

}


3. 主函数Main;测试各种方法类;
```jsx
package com.java.main;

import com.java.dao.LinkListDao;
import com.java.model.LinkList;

public class LinkListMain {
  public static void main(String[] args) {
    LinkListDao linkListDao = new LinkListDao();
    // 创建链表
    LinkList list = linkListDao.Init_LinkList();

    // 数据插入链表
    linkListDao.Insert_LinkList(list, 0, "A");
    linkListDao.Insert_LinkList(list, 1, "B");
    linkListDao.Insert_LinkList(list, 2, "C");
    linkListDao.Insert_LinkList(list, 3, "D");
    linkListDao.Insert_LinkList(list, 4, "D");

    // 打印链表
    System.out.println("插入数据之后的链表为:");
    linkListDao.Print_LinkList(list);
    System.out.println();

    // 删除指定位置的值
    linkListDao.RemoveByPos_LinkList(list, 2);

    // 打印链表
    System.out.println("删除元素C之后的链表为:");
    linkListDao.Print_LinkList(list);
    System.out.println();

    // 获得链表长度
    System.out.println("链表长度为:");
    System.out.println(linkListDao.Size_LinkList(list));

    // 查找值为3的位置
    System.out.println("值为D的位置为:");
    linkListDao.Find_LinkList(list, "D");
    System.out.println();

    // 返回第一个结点元素的值
    System.out.println("第一个结点元素为:");
    System.out.println(linkListDao.Front_LinkList(list));
  }
}