赞
踩
以上情况组合起来共有8种链表结构,不过大家不用担心,我们只需重点掌握两种:
2.创建MySingleList类并让它实现IList类中的方法
创建 MyLinkedList 类并让它实现 IList 类中的方法
在 Java 编程语言中,链表是一种基本的数据结构,它提供了一种灵活的方式来存储和管理数据。Java 中的 LinkedList 类是链表实现的一个典型例子,它是 Java 集合框架的一部分。在这篇博客中,我们将深入探讨 LinkedList 和链表的概念、特点以及如何在 Java 中使用它们。
链表是一种线性数据结构,由一系列节点(Node)组成,每个节点包含两个主要元素:数据和下一个节点的地址。链表的节点不必在内存中连续存储,它们可以分散存放,并通过存储在节点内的下一个节点的地址连接起来形成一个序列。
请注意:
单向链表的每个节点包含:
双向链表在单向链表的基础上增加了一个指向前一个节点的指针。
因此每个节点包含:
就是多了个节点用来记录链表头节点的地址。
跟前面的单向链表一样,因为就是无头单链表
就是把链表的最后一个节点用来存头节点的地址。
跟前面的单向链表和无头链表一样,因为就是无头单向链表。
链表的知识是一通百通的,其他情况大家不用担心,学完自然就懂了。
- public interface IList {
- //头插法
- void addFirst(int data);
- //尾插法
- void addLast(int data);
- //任意位置插入,第一个数据节点为0号下标
- void addIndex(int index,int data);
- //查找是否包含关键字key是否在单链表当中
- boolean contains(int key);
- //删除第一次出现关键字为key的节点
- void remove(int key);
- //删除所有值为key的节点
- void removeAllKey(int key);
- //得到单链表的长度
- int size();
- void clear();
- void display();
- }
这个接口类也可以不创建,但为了避免漏写功能还是建议创建。
- public class MySingleList implements IList {
- static class ListNode {
- public int val;
- public ListNode next;
-
- public ListNode(int val) {
- this.val = val;
- }
- }
-
- public ListNode head;//用来保存头节点地址
-
- // public void createList() {
- // ListNode listNode1 = new ListNode(1);
- // ListNode listNode2 = new ListNode(2);
- // ListNode listNode3 = new ListNode(3);
- // ListNode listNode4 = new ListNode(4);
- // ListNode listNode5 = new ListNode(5);
- //
- // listNode1.next = listNode2;
- // listNode2.next = listNode3;
- // listNode3.next = listNode4;
- // listNode4.next = listNode5;
- //
- // this.head = listNode1;
- // }
-
- //头插法
- @Override
- public void addFirst(int data) {
- ListNode listNode = new ListNode(data);
- // if (this.head == null) {
- // this.head = listNode;
- // return;
- // }
- listNode.next = this.head;
- this.head = listNode;
- }
-
- //尾插法
- @Override
- public void addLast(int data) {
- ListNode listNode = new ListNode(data);
- ListNode tmp = this.head;
- if (tmp == null) {
- this.head = listNode;
- } else {
- while (tmp.next != null) {
- tmp = tmp.next;
- }
- tmp.next = listNode;
- }
- }
-
- //任意位置插入,第一个数据节点为0号下标
- @Override
- public void addIndex(int index, int data) {
- if (index < 0 || index > size()) {
- throw new IndexIllegality("插入元素位置异常:" + index);
- }
- if (index == 0) {
- addFirst(data);
- return;
- }
- if (index == size()) {
- addLast(data);
- return;
- }
- ListNode tmp = searchPrev(index);
- ListNode listNode = new ListNode(data);
- listNode.next = tmp.next;
- tmp.next = listNode;
- }
-
- //查找是否包含关键字key是否在单链表当中
- private ListNode searchPrev(int index) {
- ListNode tmp = this.head;
- while (index - 1 != 0) {
- tmp = tmp.next;
- --index;
- }
- return tmp;
- }
-
- //查找是否包含关键字key是否在单链表当中
- @Override
- public boolean contains(int key) {
- ListNode tmp = this.head;
- while (tmp != null) {
- if (tmp.val == key) {
- return true;
- }
- tmp = tmp.next;
- }
- return false;
- }
-
- //删除第一次出现关键字为key的节点
- @Override
- public void remove(int key) {
- // if (this.head == null) {
- // System.out.println("当前链表无数据,无法进行删除!");
- // return;
- // }
- if (isEmpty()) {
- return;
- }
-
- if (this.head.val == key) {
- this.head = this.head.next;
- return;
- }
-
- ListNode tmp = findPrev(key);
- if (tmp == null) {
- System.out.println("要删除的数不存在!");
- return;
- }
- ListNode del = tmp.next;
- tmp.next = del.next;
- }
-
- //判断链表是否为空,仅做内部支持
- private boolean isEmpty() {
- if (this.head == null) {
- System.out.println("当前链表无数据,无法进行删除!");
- return true;
- }
- return false;
- }
-
- //根据key来查找前驱节点的地址,仅做内部支持
- private ListNode findPrev(int key) {
- ListNode tmp = this.head;
- while (tmp.next != null) {
- if (tmp.next.val == key) {
- return tmp;
- }
- tmp = tmp.next;
- }
- return null;
- }
-
- //删除所有值为key的节点
- @Override
- public void removeAllKey(int key) {
- if (isEmpty()) {
- return;
- }
- ListNode prev = this.head;
- ListNode tmp = this.head.next;
- while (tmp != null) {
- if (tmp.val == key) {
- prev.next = tmp.next;
- } else {
- prev = tmp;
- }
- tmp = tmp.next;
- }
- if (this.head.val == key) {
- this.head = this.head.next;
- }
- }
-
- //获得单链表的长度
- @Override
- public int size() {
- int count = 0;
- ListNode tmp = this.head;
- while (tmp != null) {
- ++count;
- tmp = tmp.next;
- }
- return count;
- }
-
- //销毁链表
- @Override
- public void clear() {
- ListNode tmp = this.head;
- this.head = null;
- while (tmp != null) {
- ListNode cur = tmp.next;
- tmp.next = null;
- tmp = cur;
- }
- }
-
- //打印全部链表,仅做测试
- @Override
- public void display() {
- ListNode tmp = this.head;
- while (tmp != null) {
- System.out.print(tmp.val + " ");
- tmp = tmp.next;
- }
- System.out.println();
- }
-
- //从指定的节点开始打印链表,仅做测试
- public void display(ListNode newHead) {
- ListNode tmp = newHead;
- while (tmp != null) {
- System.out.print(tmp.val + " ");
- tmp = tmp.next;
- }
- System.out.println();
- }
-
- //逆置链表,仅做测试
- public ListNode reverseList() {
- //为空
- if (this.head == null) {
- return null;
- }
- //只有一个节点
- if (this.head.next == null) {
- return this.head;
- }
- ListNode tmp = this.head.next;
- this.head.next = null;
- while (tmp != null) {
- ListNode tmpNext = tmp.next;
- tmp.next = this.head;
- this.head = tmp;
- tmp =tmpNext;
- }
- return this.head;
- }
- }
抛异常部分请自行完成,或用if语句代替。
MyLinkedList 同样接入 IList 接口就行,不用再写一个接口类。LinkedList 实际上是无头双向链表,因此我们实现这个无头双向链表即可。
- public class MyLinkedList implements IList {
- static class ListNode {
- public int val;
- public ListNode next;
- public ListNode prev;
-
- public ListNode(int val) {
- this.val = val;
- }
- }
-
- public ListNode head;//用来保存头节点地址
- public ListNode last;//用来保存尾节点地址
-
- //头插法
- @Override
- public void addFirst(int data) {
- ListNode tmp = new ListNode(data);
- if (this.head == null) {
- this.head = tmp;
- this.last = tmp;
- } else {
- tmp.next = this.head;
- this.head.prev = tmp;
- this.head = tmp;
- }
- }
-
- //尾插法
- @Override
- public void addLast(int data) {
- ListNode tmp = new ListNode(data);
- if (this.last == null) {
- this.head = tmp;
- this.last = tmp;
- } else {
- last.next = tmp;
- tmp.prev = this.last;
- this.last = tmp;
- }
- }
-
- //任意位置插入,第一个数据节点为0号下标
- @Override
- public void addIndex(int index, int data) throws IndexIllegality {
- int len = size();
- if (index < 0 || index > len) {
- throw new IndexIllegality("插入元素位置异常:" + index);
- }
- if (index == 0) {
- addFirst(data);
- return;
- }
- if (index == len) {
- addLast(data);
- return;
- }
- ListNode tmp = findIndex(index);
- ListNode listNode = new ListNode(data);
- listNode.next = tmp;
- tmp.prev.next = listNode;
- listNode.prev = tmp.prev;
- tmp.prev = listNode;
- }
-
- //查找指定下标(实际上是没有下标的)的节点地址,仅做内部支持
- private ListNode findIndex(int index) {
- ListNode tmp = this.head;
- while (index != 0) {
- tmp = tmp.next;
- --index;
- }
- return tmp;
- }
-
- //查找是否包含关键字key是否在单链表当中
- @Override
- public boolean contains(int key) {
- ListNode tmp = this.head;
- while (tmp != null) {
- if (tmp.val == key) {
- return true;
- }
- tmp = tmp.next;
- }
- return false;
- }
-
- //删除第一次出现关键字为key的节点
- @Override
- public void remove(int key) {
- if (isEmpty()) {
- return;
- }
- if (this.head.val == key) {
- this.head = this.head.next;
- if (this.head != null) {
- this.head.prev = null;
- }
- return;
- }
- if (this.last.val == key) {
- this.last = this.last.prev;
- if (this.last != null) {
- this.last.next = null;
- }
- return;
- }
- ListNode tmp = findKey(key);
- if (tmp == null) {
- System.out.println("要删除的数不存在!");
- return;
- }
- tmp.prev.next = tmp.next;
- tmp.next.prev = tmp.prev;
- }
-
- //根据key查找节点地址,仅做内部支持
- private ListNode findKey(int key) {
- ListNode tmp = this.head;
- while (tmp != null) {
- if (tmp.val == key) {
- return tmp;
- }
- tmp = tmp.next;
- }
- return null;
- }
-
- //判断链表是否为空,仅做内部支持
- private boolean isEmpty() {
- if (this.head == null) {
- System.out.println("当前链表无数据,无法进行删除!");
- return true;
- }
- return false;
- }
-
- //删除所有值为key的节点
- @Override
- public void removeAllKey(int key) {
- ListNode tmp = this.head;
- while (tmp != null) {
- if (tmp.val == key) {
- if (tmp == this.head) {
- this.head = this.head.next;
- if (this.head == null) {
- this.last = null;
- } else {
- this.head.prev = null;
- }
- } else {
- tmp.prev.next = tmp.next;
- if (tmp.next == null) {
- this.last = this.last.prev;
- } else {
- tmp.next.prev = tmp.prev;
- }
- }
- }
- tmp = tmp.next;
- }
- }
-
- //获得单链表的长度
- @Override
- public int size() {
- ListNode tmp = this.head;
- int count = 0;
- while (tmp != null) {
- ++count;
- tmp = tmp.next;
- }
- return count;
- }
-
- //销毁链表
- @Override
- public void clear() {
- ListNode tmp = this.head;
- this.head = null;
- this.last = null;
- while (tmp != null) {
- ListNode cur = tmp.next;
- tmp.next = null;
- tmp.prev = null;
- tmp = cur;
- }
- }
-
- //打印全部链表,仅做测试
- @Override
- public void display() {
- ListNode tmp = this.head;
- while (tmp != null) {
- System.out.print(tmp.val + " ");
- tmp = tmp.next;
- }
- System.out.println();
- }
- }
跟上面一样,抛异常部分请自行完成,或用if语句代替。
LinkedList 的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
在集合框架中,LinkedList 也实现了 List 接口,具体如下:
请注意:
方法 | 解释 |
LinkedList() | 无参构造 |
public LinkedList(Collection<? extends E> c) | 使用其他集合容器中元素构造 List |
- public static void main(String[] args) {
- List<Integer> list1 = new LinkedList<>();//通过接口new
- LinkedList<String> list2 = new LinkedList<>();//直接new
- List<String> list3 = new java.util.ArrayList<>();//使用其他集合容器中元素构造List
-
- list2.add("Java");
-
- //使用ArrayList构造LinkedList
- List<String> list4 = new LinkedList<>(list3);
- //使用LinkedList构造LinkedList
- List<String> list5 = new LinkedList<>(list2);
- }
个人推荐使用第一种和第二种方法。
方法 | 解释 |
boolean add(E e) | 尾插 e |
void add(int index, E element) | 将 e 插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 尾插 c 中的元素 |
E remove(int index) | 删除 index 位置元素 |
boolean remove(Object o) | 删除遇到的第一个 o |
E get(int index) | 获取下标 index 位置元素 |
E set(int index, E element) | 将下标 index 位置元素设置为 element |
void clear() | 清空 |
boolean contains(Object o) | 判断 o 是否在线性表中 |
int indexOf(Object o) | 返回第一个 o 所在下标 |
int lastIndexOf(Object o) | 返回最后一个 o 的下标 |
List<E> subList(int fromIndex, int toIndex) | 截取部分 list |
代码演示如下:
- public static void main(String[] args) {
- List<Integer> list = new LinkedList<>();//通过List接口来new一个LinkedList
- list.add(1);
- list.add(2);
- list.add(3);
-
- //使用for循环
- for (int i = 0; i < list.size(); i++) {
- System.out.print(list.get(i) + " ");
- }
- System.out.println();
-
- //使用for-each(增强for循环)
- for (Integer integer : list) {
- System.out.print(integer + " ");
- }
- System.out.println();
-
- //使用迭代器
- Iterator<Integer> it = list.listIterator();//迭代器
- while (it.hasNext()) {
- System.out.print(it.next() + " ");
- }
- System.out.println();
-
- //使用反向迭代器---反向遍历
- ListIterator<Integer> rit = list.listIterator(list.size());
- while (rit.hasPrevious()){
- System.out.print(rit.previous() +" ");
- }
- }
运行结果如下:
不同点 | ArrayList | LinkedList |
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定连续 |
随机访问 | 支持O(1) | 不支持:O(N) |
头插 | 需要搬移元素,效率低O(N) | 只需修改引用的指向,时间复杂度为O(1) |
插入 | 空间不够时需要扩容 | 没有容量的概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除频繁 |
LinkedList 是 Java 中实现链表概念的一个强大工具。它提供了一系列方法来高效地进行数据的插入、删除和访问操作。了解 LinkedList 的工作原理和如何在 Java 中使用它对于开发高效的程序至关重要。无论是在学术研究还是实际开发中,LinkedList 都是一个不可或缺的组件。
以上,就是的本次要带大家深度剖析的Java中的LinkedList和链表的全部内容,感谢大家愿意花时间阅读本文!如有错误,建议,或问题均可在评论区指出!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。