Day 09 List子接口LinkedList

List子接口LinkedList

LinkedList引入

ArrayList : 底层是数组,通过下标就能查询,它的查询比较快,但是删除比较慢 LinkedList:底层是双向链表,要一个一个的遍历,没有下标,所以数据比较多的时候查询比较 慢,但是它比较适合添加和删除操作。

  • 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高

  • LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末 元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。

LinkeList前置基础知识

单项链表

element:用来存放元素

next:用来指向下一个节点 通过每个节点的指针指向下一个节点从而连接起来的结构,最后一个节点的next指向null

在这里插入图片描述

单向循环链表

在单向链表的最后一个节点的next会指向头节点,而不是指向null,这样存成一个环。

在这里插入图片描述

双向链表

element:用来存放元素

pre:用来指向上一个节点

next:用来指向下一个节点 双向链表包含两个指针,pre指向前一个节点, next 指向后一个指针,但是第一个节点head(头节点) 的pre指向null,最后一个节点的tail指向null。

在这里插入图片描述

双向循环链表

lement、pre、next 跟前面的一样

第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,也形成一个“环”。
在这里插入图片描述

LinkedList常用方法

方法名说明
void addFirst(Object o)在链表的头部添加元素
void addLast(Object o)在链表的尾部添加元素
Object getFirst()获取链表中的第一个元素
Object getLast()获取链表中的最后一个元素
Object removeFirst删除中的第一个元素
Object removeLast()删除链表中的最后一个元素
public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add("aaa");//添加到数组
        linkedList.add("bbb");
        linkedList.add("ccc");
        linkedList.add("ddd");
        linkedList.add("eee");
        System.out.println(linkedList);//打印出添加后的数组
        //linkedList.clear();
        linkedList.offerFirst("eee");//在首位添加元素
        System.out.println(linkedList);
        linkedList.addFirst("qqq");//继续在首位添加元素
        System.out.println(linkedList);
        linkedList.removeFirst();//移除首位元素
        System.out.println(linkedList);
        linkedList.pollFirst();
        //方法检索并删除此列表的第一个元素,如果此列表为空,则返回 null
        System.out.println(linkedList);
    }

链表的原理及实现

什么是链表?

链表是由n个子节点组合起来的一种线性数据结构 链表的特性:

  • 链表是以节点(Node)的方式来存储,所以又叫链式存储
  • 节点可以连续存储,也可以不连续存储
  • 节点的逻辑顺序与物理顺序可以不一致
  • 链表可以扩充

单链表

两个部分:

  • data域:数据域,用来存储元素数据
  • next域:用于指向下一节点

在这里插入图片描述

单链表的操作

单链表的所有操作都是从head开始,head本身不存储元素,其next指向第一个节点,然后顺着next链表 进行一步步操作。其尾部节点的next指向为空,这也是判断尾部节点的依据。

插入节点

向单链表中插入一个新节点,可以通过调整两次next指向来完成。

x为新节点,将其next指向A2,再将A1的next指向X即可。

如果是从尾节点插入,直接将尾节点的next指向新节点就可以了。

在这里插入图片描述

删除操作

从单链表中删除一节点,通过修改next指向来实现。

将A1的next 指向为A2,这样就能删除 X, X的内存空间会自动被垃圾回收。

在这里插入图片描述

代码实现
public class Node {
    public String data;
    public Node next;
    public Node(String data) {
        this.data = data;
    }
    public String getData() {
        return data;
    }
    public void setData(String data) {
        this.data = data;
    }
    public Node getNext() {
        return next;
    }
    public void setNext(Node next) {
        this.next = next;
    }
    @Override
    public String toString() {
        return "Node{" +
                "data='" + data + '\'' +
                ", next=" + next +
                '}';
    }
}
 private Node head = new Node(null);//得有head节点,是单链表的开始
 

//添加节点
public void add(Node node){
        Node temp = head;//temp存储头节点
        while (true) {//判断node是否为空
            if (temp.next == null){
                temp.next = node;
                break;
            }
            temp = temp.next;
        }
    }

在这里插入图片描述

singleLinkedList.add(aaa);
singleLinkedList.add(bbb);

在这里插入图片描述

在这里插入图片描述

    //指定节点插入元素
    public void addOfOrder(Node node, String name) {
        Node temp = head;
        while (true) {
            if (temp.next == null){
                temp.next = node;
                break;
            }else if (name == temp.getData()) {
                node.next = temp.next;
                temp.next = node;
                break;
            }
            temp = temp.next;
        }
    }
singleLinkedList.addOfOrder(eee,"bbb");

在这里插入图片描述

//查找获取数据
public Node get(String key) {
    if (head.next == null) {
        return null;
    }
    Node temp = head.next;
    while (temp != null){
        if (temp.data == key) {
            return temp;
        }
        temp = temp.next;
    }
    return null;
}
//移除数据
   public void remove(Node node) {
        Node temp = head.next;
        while (true) {
            if (temp.next == null) {
                break;
            }
            if (temp.next.data == node.data) {
                temp.next = temp.next.next;//断开连接
                break;
            }
            temp = temp.next;
        }
    }

在这里插入图片描述

//修改叫这个node节点的名字name
public void update(Node node, String name) {
        Node temp = head.next;//这句话一般都不变,都要用
        while (true) {
            if (temp == null) {
                break;
            }
            if (temp.data == name) {
                temp.data = node.data;
                break;
            }
            temp = temp.next;
        }
    }
//测试
public static void main(String[] args) {
        Node aaa = new Node("aaa");
        Node bbb = new Node("bbb");
        Node ccc = new Node("ccc");
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.add(aaa);
        singleLinkedList.add(bbb);
        singleLinkedList.add(ccc);
        singleLinkedList.print();
        System.out.println("=====================");
        Node eee = new Node("eee");
        singleLinkedList.addOfOrder(eee,"bbb");
        singleLinkedList.print();
        System.out.println("=====================");
        System.out.println(singleLinkedList.get("eee"));
        System.out.println(singleLinkedList.get("qqq"));
        System.out.println("=====================");
        singleLinkedList.remove(ccc);
        singleLinkedList.print();
        System.out.println("=====================");
        singleLinkedList.update(new Node("www"), "eee");
        singleLinkedList.print();

双向链表

双向链表其节点由三部分构成:

  • prev域:用于指向上一节点

  • data域:数据域,用来存储元素数据

  • next域:用于指向下一节点

在这里插入图片描述

双向链表的操作

双向链表的操作可以从两端开始,从第一个节点通过next指向可以一步步操作到尾部,从最后一个节点 通过prev指向可以一步步操作到头部。

插入节点

像双向链表中插入一个节点,需要调整两次 pre指向 和 两次 next 指向。

X为新节点,将A1的next指向X,将x的next指向A2,将A2的pre指向X,将X 的pre指向A1。

在这里插入图片描述

删除节点

从双向链表中删除一个节点,需要调整一次 pre 指向 和 一次 next 指向就可以了。

将A1的 next指向 A2, 将A2的pre 指向 A1。

在这里插入图片描述

代码实现
public class DoubleNode {
    public final int key;
    public String value;
    public DoubleNode pre;
    public DoubleNode next;
    public DoubleNode(int key, String value) {
        this.key = key;
        this.value = value;
    }
    @Override
    public String toString() {
        return "DoubleNode{" +
                "key=" + key +
                ", value='" + value + '\'' +
                '}';
    }
}

public class DoubleLinkedList {
    public DoubleNode first = null; //双链表的头部,也就是第一个节点
    public DoubleNode last = null; //双链表的尾部,也就是最后一个节点

    /**
     * @Description //TODO 从尾部添加节点
     */
    public DoubleLinkedList addToTail(DoubleNode node) {
        if (last == null) {
            first = node;
        } else {
            last.next = node;
            node.pre = last;
        }
        last = node;
        return this;
    }

    /**
     * @Description //TODO 从头部开始添加
     */
    public DoubleLinkedList addToHead(DoubleNode node) {
        if (first == null) {
            last = node;
        } else {
            node.next = first;
            first.pre = node;
        }
        first = node;
        return this;
    }

    /**
     * @Description //TODO 按照key key顺序添加
     */
    public DoubleLinkedList addOfOrder(DoubleNode node) {
        if (first == null) {
            first = node;
            last = node;
            return this;
        }
//node 比头节点小,将 node 设为头肩
        if (first.key > node.key) {
            first.pre = node;
            node.next = first;
            first = node;
            return this;
        }
//node 比尾节点大,将node变为 尾节点
        if (node.key > last.key) {
            last.next = node;
            node.pre = last;
            last = node;
            return this;
        }
        DoubleNode temp = first.next;
        while (true) {
            if (temp.key > node.key) {
                node.next = temp;
                node.pre = temp.pre;
                temp.pre.next = node;
                temp.pre = node;
                break;
            }
            temp = temp.next;
        }
        return this;
    }

    /**
     * @Description //TODO 获取节点
     */
    public DoubleNode get(int key) {
        if (first == null) {
            return null;
        }
        DoubleNode temp = first;
        while (temp != null) {
            if (temp.key == key) {
                return temp;
            }
            temp = temp.next;
        }
        return null;
    }

    /**
     * @Description //TODO 删除
     */
    public DoubleLinkedList remove(DoubleNode node) {
        if (first == null) {
            return this;
        }
//如果删除的是头节点
        if (first == node) {
            first.next.pre = null;
            first = first.next;
            return this;
        }
//如果删除的是为节点
        if (last == node) {
            last.pre.next = null;
            last = last.pre;
            return this;
        }
        DoubleNode temp = first.next;
        while (temp != null) {
            if (temp.key == node.key) {
                temp.pre.next = temp.next;
                temp.next.pre = temp.pre;
                break;
            }
            temp = temp.next;
        }
        return this;
    }

    /**
     * @Description //TODO
     */
    public DoubleLinkedList update(DoubleNode node) {
        if (first == null) {
            return this;
        }
        DoubleNode temp = first;
        while (temp != null) {
            if (temp.key == node.key) {
                temp.value = node.value;
                break;
            }
            temp = temp.next;
        }
        return this;
    }

    public void print() {
        if (first == null) {
            return;
        }
        DoubleNode temp = first;
        while (temp != null) {
            System.out.println(temp);
            temp = temp.next;
        }
    }

    public static void main(String[] args) {
        DoubleNode aaa = new DoubleNode(1, "aaa");
        DoubleNode bbb = new DoubleNode(2, "bbb");
        DoubleNode ccc = new DoubleNode(6, "ccc");
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
        doubleLinkedList.addToTail(aaa);
        doubleLinkedList.addToTail(bbb);
        doubleLinkedList.addToTail(ccc);
        doubleLinkedList.print();
        System.out.println("===================");
        DoubleNode ddd = new DoubleNode(4, "ddd");
        doubleLinkedList.addToHead(ddd);
        doubleLinkedList.print();
        System.out.println("===================");
        DoubleNode eee = new DoubleNode(3, "eee");
        doubleLinkedList.addOfOrder(eee);
        doubleLinkedList.print();
        System.out.println("===================");
        System.out.println(doubleLinkedList.get(7));
        System.out.println("===================");
        doubleLinkedList.remove(new DoubleNode(1, "aaa"));
        doubleLinkedList.print();
        System.out.println("===================");
        doubleLinkedList.update(new DoubleNode(2, "herb"));
        doubleLinkedList.print();
    }
}

源码分析

LinkedList特性

  1. LinkedList 集合底层实现的数据结构为双向链表
  2. LinkedList 集合中元素允许为 null
  3. LinkedList 允许存入重复的数据
  4. LinkedList 中元素存放顺序为存入顺序。
  5. LinkedList 是非线程安全的
  6. 如果在频繁的插入,或者删除数据时,就用linkedList性能会更好。

继承结果及层次关系

在这里插入图片描述

类的属性

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
//LinkedList 中的节点个数
transient int size = 0;
//LinkedList 链表的头节点
transient Node<E> first;
//LinkedList 链表的 尾节点
transient Node<E> last;
}

构造方法

  1. 空构造器
//空构造器生成一个空链表 ==> frist = last = null
public LinkedList() {
}

  1. 有参构造器
// 将集合 c 中的 各个元素构建成 LinkedList 链表
public LinkedList(Collection<? extends E> c) {
//调用无参构造器
this();
//添加集合中的所有元素
addAll(c);
}

内部类(Node)

private static class Node<E> {
     E item; //节点的值
     Node<E> next; //用于指向下一节点(后继)
     Node<E> prev;//用于指向上一节点(前驱)
     //构造函数,赋值 前驱后继
     Node(Node<E> prev, E element, Node<E> next) {
     this.item = element;
     this.next = next;
     this.prev = prev;
    }
}

说明:内部类 Node 就是实际的节点,用于存放实际元素的地方

add()

public boolean add(E e) {
//将元素添加到链表的末尾
linkLast(e);
return true;
}

说明:add函数用于向LinkedList中添加一个元素,并且添加到链表尾部。具体添加到尾部的逻辑是由 linkLast函数完成的。

    void linkLast(E e) {
        //临时节点 l(L的小写)保存last,也就是 l 指向最后一个节点
        final Node<E> l = last;
        //将e(值)封装为节点,并且e.perv指向了最后一个节点
        final Node<E> newNode = new Node<>(l, e, null);
        //newNode 成为最后一个节点
        last = newNode;
        // 判断之前的链表是否为空,如果为空,那么 newNode 就成为了头节点
        if (l == null)
            first = newNode;
        else //否则将之前的尾节点 的 next 指向 newNode(新节点)
            l.next = newNode;
        size++;///添加了节点,size要自增
        modCount++;
    }

的末尾
linkLast(e);
return true;
}


说明:add函数用于向LinkedList中添加一个元素,并且添加到链表尾部。具体添加到尾部的逻辑是由 linkLast函数完成的。

void linkLast(E e) {
    //临时节点 l(L的小写)保存last,也就是 l 指向最后一个节点
    final Node<E> l = last;
    //将e(值)封装为节点,并且e.perv指向了最后一个节点
    final Node<E> newNode = new Node<>(l, e, null);
    //newNode 成为最后一个节点
    last = newNode;
    // 判断之前的链表是否为空,如果为空,那么 newNode 就成为了头节点
    if (l == null)
        first = newNode;
    else //否则将之前的尾节点 的 next 指向 newNode(新节点)
        l.next = newNode;
    size++;///添加了节点,size要自增
    modCount++;
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值