- ArrayList的缺陷
 
通过ArrayList上节课的学习,我们了解到如果ArrayList要删除或插入一个元素,后面的元素都要进行移动,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置的插入和删除操作比较多的场景。因此java集合又引入了ListedList,即链表结构。
- 链表
 
2.1链表的结构
链表是一种物理储存上非连续的储存结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
   【注意】
从图中可以看出,链表结构在逻辑上是连续的,但是在物理上是不连续的;
实际中的节点都是在堆上的;
从堆上申请的空间,是按照一定的策略进行分配的,两个节点的储存空间可能是连续的可能是不连续的(链表的优点就是不需要物理上连续的大块空间)。
现实中链表的种类很多,但基本就以下8种情况:
单向或双向
   带头或者不带头
   循环或者非循环
   这3种大的类型再任意搭配,如:单向不带头非循环链表,双向不带头循环链表等。但是我们重点掌握的只有两种:
无头单项非循环链表:结构简单,一般不会用来储存数据,实际中更多的是作为其他数据结构的子结构,但是这种结构在笔试和面试中常见。
   无头双向链表:在Java中的集合框架LinkedList底层的实现都是无头双向循环链表。
2.2无头单向非循环链表的实现
MyLinkedList的简单实现
package Demo1;
import java.util.LinkedList;
/**
 * Describe:这里简单实现一下LinkedList
 * User:lenovo
 * Date:2023-01-04
 * Time:16:21
 */
class ListNode {
    public int val;
    public ListNode next;
    public ListNode() {}
    public ListNode(int val) {
        this.val = val;
    }
}
public class MyLinkedList {
    ListNode head;
    //为了下面的更好的调试,我们自己创造一个方法,来打印这个链表,但是这个方法并不是有的
    public void display() {
        ListNode cur = head;
        while(cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
    //头插法
    public void addFirst(int data) {
        ListNode cur = new ListNode(data);
        cur.next = head;
        head = cur;
    }
    //尾插法
    public void addLast(int data) {
        ListNode cur = head;
        //判断链表是否为空
        if(head == null) {
            head = new ListNode(data);
        }
        //找到链表的最后一个节点
        while(cur.next != null) {
            cur = cur.next;
        }
        ListNode tmp = new ListNode(data);
        cur.next = tmp;
    }
    //任意位置插入,第一个节点为0号下表
    public void addIndex(int index, int data) {
        if(index < 0) {
            throw new ListIndexOutOfException("请检查坐标合法性");
        }
        //如果原链表为空
        if(head == null) {
            head = new ListNode(data);
            return;
        }
        ListNode cur = head;
        ListNode tmp = new ListNode(data);
        int count = 0;
        //如果是头插
        if(index == 0) {
            tmp.next = head;
            head = tmp;
            return;
        }
        //不是头插
        while(cur != null) {
            if(count + 1 == index) {
                tmp.next = cur.next;
                cur.next = tmp;
                return;
            }else {
                count++;
                cur = cur.next;
            }
        }
        if(count != index) {
            throw new ListIndexOutOfException("请检查坐标的合法性");
        }
    }
    //查找关键字key是否在单链表中
    public boolean contains(int key) {
        if(head == null) {
            return true;
        }
        ListNode cur = head;
        while(cur != null) {
            if(cur.val == key) {
                return true;
            }else {
                cur = cur.next;
            }
        }
        return false;
    }
    //删除第一次出现关键字的节点
    public void remove(int key) {
        if(head == null) {
            return;
        }
        ListNode cur = head;
        //第一个节点判断
        if(head.val == key) {
            head = head.next;
            return;
        }
        //其他节点的判断
        while(cur.next != null) {
            if(cur.next.val == key) {
                cur.next = cur.next.next;
                return;
            }else {
                cur = cur.next;
            }
        }
    }
    //删除所有的关键字key的节点
    public void removeAllKey(int key) {
        //链表为空
        if(head == null) {
            return;
        }
        //除了第一个节点,其他节点的判断
        ListNode cur = head;
        while(cur.next != null) {
            if(cur.next.val == key) {
                cur.next = cur.next.next;
            }else {
                cur = cur.next;
            }
        }
        //第一个节点的判断
        if(head.val == key) {
            head = head.next;
        }
    }
    //清除所有节点
    public void clear() {
        head = null;
    }
    //得到单链表的长度
    public int size() {
        int count = 0;
        ListNode cur = head;
        while(cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }
}
 异常类型
package Demo1;
/**
 * Describe:
 * User:lenovo
 * Date:2023-01-04
 * Time:19:13
 */
public class ListIndexOutOfException extends RuntimeException{
    public ListIndexOutOfException() {}
    public ListIndexOutOfException(String message) {
        super(message);
    }
}
 对代码进行测试
import Demo1.MyLinkedList;
import java.util.ArrayList;
import java.util.LinkedList;
/**
 * Describe:
 * User:lenovo
 * Date:2023-01-04
 * Time:15:47
 */
public class Test {
    public static void main(String[] args) {
        MyLinkedList a = new MyLinkedList();
        System.out.println("============头插法============");
        a.addFirst(6);
        a.addFirst(5);
        a.addFirst(4);
        a.addFirst(3);
        a.addFirst(2);
        a.addFirst(1);
        a.display();
        System.out.println("===========尾插法==============");
        a.addLast(7);
        a.addLast(8);
        a.display();
        System.out.println("==========插入任意位置========");
        a.addIndex(0,0);
        a.addIndex(1,1);
        a.addIndex(10,9);
        a.display();
        System.out.println("==========在链表中查找是否包含关键字=======");
        System.out.println(a.contains(0));
        System.out.println(a.contains(99));
        System.out.println("============删除第一个0============");
        a.remove(0);
        a.remove(99);
        a.display();
        System.out.println("===========删除所有的1============");
        a.removeAllKey(1);
        a.removeAllKey(9);
        a.display();
        System.out.println("===========获得链表的长度==========");
        System.out.println(a.size());
        System.out.println("==============清空链表=============");
        a.clear();
        a.display();
    }
}
 结果展示
   3.单项链表相关习题
我会以博客的形式为大家展现,完成后会将链接放在下面。
希望大家多多鼓励和支持!!!!







![[Vue的数据绑定]一.Vue的数据绑定;二.Vue的事件绑定;三.Class和Style的绑定;四.Vue的过滤器;五.Vue脚手架的使用](https://img-blog.csdnimg.cn/img_convert/08076e28d78c39db246c534ffc74faed.png)











