Java进阶(7)——手动实现LinkedList 内部node类的实现 增删改查的实现 toString方法 源码的初步理解

news2025/4/7 21:19:45

目录

  • 引出
  • 从ArrayList到Linkedlist
    • 手动实现ArrayList
    • 从ArrayList到LinkedList
  • 总体设计
    • Node类
    • Node的方法:根据index找node
  • 增删改查的实现
    • 增加元素
    • 删除元素
    • 修改元素
    • 查询元素
  • toString方法
  • 完整代码
    • List接口类
    • LinkedList的实现
    • 测试类
  • 总结

引出


1.linkedList的节点,当前,上一个,下一个的思想;
2.根据index找node的方法,根据index确定从头部还是尾部;
3.linkedlist的增删改查的实现,本质是改变节点的信息;
4.递归方法实现自定义链表的toString方法;

从ArrayList到Linkedlist

在这里插入图片描述

手动实现ArrayList

Java进阶(3)——手动实现ArrayList & 源码的初步理解分析 & 数组插入数据和删除数据的问题

在这里插入图片描述

从ArrayList到LinkedList

如果发生对表的一些插入和删除操作,特别是对表的前端进行,那么数组就不是一种好的选择。另一种数据结构:链表(linked list)。

在这里插入图片描述

链表由一系列节点组成,这些节点不必在内存中相连。每一个节点均含有表元素和到包含该元素后继元的节点的链(link)。我们称之为next链。最后一个单元的next链引用null。

在这里插入图片描述

链表的插入

在这里插入图片描述

让每一个节点持有一个指向它在表中的前驱节点的链,我们称之为双链表(doubly linked list)。

在这里插入图片描述

总体设计

在这里插入图片描述

在考虑设计方面,我们将需要提供三个类:
1.MyLinkedList类本身,它包含到两端的链、表的大小以及一些方法。
2.Noe类,它可能是一个私有的嵌套类。一个节点包含数据以及到前一个节点的链和到下一个节点的链,还有一些适当的构造方法。
3.LinkedListIterator类,该类抽象了位置的概念,是一个私有类,并实现接口Iterator。它提供了方法next、hasNext和remove的实现。

标记节点:

前端创建一个额外的节点,逻辑上代表开始的标记。这些额外的节点有时候就叫作标记节点(sentinel node);特别地,在前端的节点有时候也叫作头节点(header node),而在末端的节点有时候也叫作尾节点(tail node)

在这里插入图片描述

Node类

私有的内部类

  • 当前节点,前置节点,后续节点;
  • 表示链表中的一个基本元素;

在这里插入图片描述

/**
     * 内部类,节点;属性,当前节点,前置节点,后续节点
     * @param <T>
     */
    private static class Node<T> {
        T item; // 当前的节点
        Node<T> next; // 下一个节点
        Node<T> prev; // 前置节点

        Node(Node<T> prev,T element,Node<T> next) {
            this.item = element;
            this.prev = prev;
            this.next = next;
        }

        @Override
        public String toString() {
            String nextStr = null;
            if (next!=null){
                nextStr = next.item.toString();
            }
            String prevStr = null;
            if (prev!=null){
                prevStr = prev.item.toString();
            }

            return "Node{" +
                    " prev=" + prevStr +
                    ",item=" + item +
                    ", next=" + nextStr +
                    '}';
        }
    }

在这里插入图片描述

Node的方法:根据index找node

思路:从头部开始找,进行循环

    public Node<T> NodeByIndex(int index){
        Node<T> x = first; // 从头部开始找
        for (int i = 0; i<index;i++){
            x = x.next;
        }
        return x;
    }

源码采用如下策略

  • 1.根据index和list的size确定从头部还是尾部找;
  • 2.根据index找node节点;

在这里插入图片描述

分析:

降低了复杂度:

如果都从头部找,时间复杂度就是O(i),在最极端的情况下,根据index找最后一个,时间复杂度是O(N);

如果是先确定从头部找,还是从尾部找,则时间复杂度最大是O(N/2);

增删改查的实现

增加元素

最简单的情况,都是从尾部新增元素

  • 1.新的节点的上一个节点为之前的尾部节点;
  • 2.新的尾部节点为当前新增的节点;
  • 3.如果是第一个节点,则需要把first设置为当前的节点
  • 4.链表的size+1;

在这里插入图片描述

    @Override
    public void add(T e) {
        Node<T> l = last;
        Node<T> newNode = new Node<>(l, e, null); // 新增的节点,尾部添加

        last = newNode;
        if (l==null){
            // 是第一个节点
            first = newNode;
            System.out.println("FirstNode --->"+first);
        }else {
            l.next = newNode;
            System.out.println("Add {"+e+"} ---->"+l);
        }
        size++;
    }

更一般的情况如下,插入一个元素

在这里插入图片描述

删除元素

删除头部?尾部?中间

  • 1.如果删除的是头部节点,则让被删除的节点的下一个节点为first节点;
  • 2.如果删除的尾部节点,则让被删除的节点的上一个节点的下一个节点为null;
  • 3.如果删除的是中间的节点,让该节点的前置节点的下一个节点指向该节点的下一个节点;

在这里插入图片描述

    @Override
    public void remove(int index) {
        // 找到要删除的节点,前置节点,后续节点
        // 1.如果删除的是头部节点
        if (index==0){
            first = NodeByIndex(index+1);
            return;
        }
        // 2.如果不是头部节点
        Node<T> tNode = NodeByIndex(index); // 当前节点
        Node<T> prev = tNode.prev; // 当前节点的上一个节点
        Node<T> next = tNode.next; // 当前节点的下一个节点
        if (next==null){
            // 删除的是尾部节点
            prev.next = null;
            return;
        }
        // 删除的是中间的某个节点
        // 让该节点的前置节点的下一个节点指向该节点的下一个节点
        prev.next = next;
        next.prev = prev;
        size--;
    }

修改元素

被修改的节点的节点关系不变,只需要把当前节点的元素变为最新的元素即可

在这里插入图片描述

代码实现

在这里插入图片描述

查询元素

调用node方法即可

    @Override
    public T get(int index) {
        // 索引不能大于等于size
        if (index<0 || index>=size){
            throw new IndexOutOfBoundsException("LinkedList的索引越界");
        }
        return NodeByIndex(index).item;
    }

toString方法

在这里插入图片描述

完整代码

List接口类

package com.tianju.book.jpa.mlist;

/**
 * 手工打造ArrayList
 */
public interface MyList<T> {
    /**
     * 增加一个元素,涉及到容量的变化
     */
    void add(T t);

    /**
     * 根据索引删除元素
     * @param index 要删除元素的索引,超过索引?索引不存在?
     */
    void remove(int index);

    /**
     * 根据索引修改一个元素
     * @param index 要修改的索引
     * @param t 修改的值
     */
    void set(int index,T t);

    /**
     * 根据索引获取元素
     * @param index 索引
     * @return 获取的元素
     */
    T get(int index);

    int size();

    String toString();

}

LinkedList的实现

package com.tianju.book.jpa.myLinkList;

import com.tianju.book.jpa.mlist.MyList;

public class MyLinkedList<T> implements MyList<T> {

    int size = 0;

    Node<T> first; // 头部节点

    Node<T> last; // 尾部节点


    /**
     * 内部类,节点;属性,当前节点,前置节点,后续节点
     * @param <T>
     */
    private static class Node<T> {
        T item; // 当前的节点
        Node<T> next; // 下一个节点
        Node<T> prev; // 前置节点

        Node(Node<T> prev,T element,Node<T> next) {
            this.item = element;
            this.prev = prev;
            this.next = next;
        }

        @Override
        public String toString() {
            String nextStr = null;
            if (next!=null){
                nextStr = next.item.toString();
            }
            String prevStr = null;
            if (prev!=null){
                prevStr = prev.item.toString();
            }

            return "Node{" +
                    " prev=" + prevStr +
                    ",item=" + item +
                    ", next=" + nextStr +
                    '}';
        }
    }


    @Override
    public void add(T e) {
        Node<T> l = last;
        Node<T> newNode = new Node<>(l, e, null); // 新增的节点,尾部添加

        last = newNode;
        if (l==null){
            // 是第一个节点
            first = newNode;
            System.out.println("FirstNode --->"+first);
        }else {
            l.next = newNode;
            System.out.println("Add {"+e+"} ---->"+l);
        }
        size++;
    }

    public Node<T> NodeByIndex(int index){
        Node<T> x = first; // 从头部开始找
        for (int i = 0; i<index;i++){
            x = x.next;
        }
        return x;
    }

    @Override
    public void remove(int index) {
        // 找到要删除的节点,前置节点,后续节点
        // 1.如果删除的是头部节点
        if (index==0){
            first = NodeByIndex(index+1);
            return;
        }
        // 2.如果不是头部节点
        Node<T> tNode = NodeByIndex(index); // 当前节点
        Node<T> prev = tNode.prev; // 当前节点的上一个节点
        Node<T> next = tNode.next; // 当前节点的下一个节点
        if (next==null){
            // 删除的是尾部节点
            prev.next = null;
            return;
        }
        // 删除的是中间的某个节点
        // 让该节点的前置节点的下一个节点指向该节点的下一个节点
        prev.next = next;
        next.prev = prev;
        size--;
    }

    @Override
    public void set(int index, T element) {
        // 索引不能大于等于size
        if (index<0 || index>=size){
            throw new IndexOutOfBoundsException("LinkedList的索引越界");
        }
        Node<T> tNode = NodeByIndex(index); // 当前节点
        T oldVal = tNode.item; // 获取旧的元素
        tNode.item = element; // 把当前节点的元素设置成新的
//        System.out.println(oldVal);
    }

    @Override
    public T get(int index) {
        // 索引不能大于等于size
        if (index<0 || index>=size){
            throw new IndexOutOfBoundsException("LinkedList的索引越界");
        }
        return NodeByIndex(index).item;
    }

    @Override
    public int size() {
        return size;
    }

    /**
     * 为了实现toString方法
     */

    String str = "null-->";

    // 通过第一个节点递归调用,获得LinkedList的链
    private String recursion(Node<T> first){

        if (!str.contains(first.item.toString())){
            str = str + first.item;
        }
        if (first.next==null){
            return str + "-->null";
        }
        str = str + "-->" +  first.next.item.toString();
        first = first.next;
        return recursion(first);
    }


    // 在每次调用后把str归位;
    private void backStr(){
        str = "null-->";
    }

    @Override
    public String toString() {
        String recursion = recursion(first);
        backStr();
        return "MyLinkedList{ " + recursion +" }";
    }
}

测试类

package com.tianju.book.jpa.myLinkList;

import org.hibernate.event.spi.SaveOrUpdateEvent;

import java.util.List;

public class MyLinkedListTest1 {
    public static void main(String[] args) {
        MyLinkedList<String> list = new MyLinkedList<>();
        list.add("PET1");
        list.add("PET2");
        list.add("PET3");
        System.out.println("**********");
        System.out.println(list);

        list.add("PET4");
        System.out.println(list);
        System.out.println(list.size());

//        System.out.println(list.get(4));
//        list.remove(0);
//        System.out.println("删除头部节点");
//        System.out.println(list);
//
//        System.out.println("删除尾部节点");
//        list.remove(3-1);
//        System.out.println(list);

        System.out.println("删除中间的节点");
        list.remove(2);
        System.out.println(list);

        System.out.println("进行set");
        list.set(0, "SHI1");
        System.out.println(list);

    }
}

在这里插入图片描述


总结

1.linkedList的节点,当前,上一个,下一个的思想;
2.根据index找node的方法,根据index确定从头部还是尾部;
3.linkedlist的增删改查的实现,本质是改变节点的信息;
4.递归方法实现自定义链表的toString方法;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/918706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity之 Vector3 的详细介绍以及方法的介绍

文章目录 总的介绍小试牛刀相关的描述的参数看个小例子 总的介绍 当涉及到Unity中的Vector3类时&#xff0c;以下是一些常用的方法和操作&#xff1a; magnitude 方法&#xff1a;返回向量的长度。 float length vector.magnitude;sqrMagnitude 方法&#xff1a;返回向量的平…

Sakana AI致力于打造日本人工智能界的top公司

原创 | 文 BFT机器人 01 Sakana AI创始人 Sakana AI 是一家总部位于东京的突破性创业公司&#xff0c;由前谷歌Brain研究员David Ha和Llion Jones共同创立。David Ha拥有多年且丰富的经验&#xff0c;曾担任Stability AI Ltd的研究负责人&#xff0c;并在Google LLC的日本人工…

ClickHouse领域集大成之作:《ClickHouse入门、实战与进阶》(文末送书)

前言 ClickHouse是大数据实时分析领域的主流选择之一。ClickHouse的目标是向人们提供世界上最快的分析型数据库。在各种OLAP查询引擎评测中&#xff0c;ClickHouse的查询性能横扫各大OLAP数据库引擎&#xff0c;尤其是Ad Hoc即席查询性能&#xff0c;一直遥遥领先。因此&#…

jenkins运行pytest测试用例脚本报错:没有权限,无法写日志PermissionError:[Error 13]Permission denied

报错信息&#xff1a; PermissionError:[Error 13]Permission denied&#xff1a;‘/var/jenkins_home/workspace/deleverySystem/Delivery_System/out_files/logs/waimai_20230823.log’ 解决方法&#xff1a; 在jenkins容器内部输入 chmod -R 777 /var/jenkins_home/works…

Ivanti曝新的MobileIron零日漏洞,正在被恶意利用

美国 IT 软件公司 Ivanti 今天提醒客户&#xff0c;一个关键的 Sentry API 身份验证绕过漏洞正在被恶意利用。 Ivanti Sentry&#xff08;前身为 MobileIron Sentry&#xff09;在 MobileIron 部署中充当 Microsoft Exchange Server 等企业 ActiveSync 服务器或 Sharepoint 服…

Diffusion Models for Image Restoration and Enhancement – A Comprehensive Survey

图像恢复与增强的扩散模型综述 论文链接&#xff1a;https://arxiv.org/abs/2308.09388 项目地址&#xff1a;https://github.com/lixinustc/Awesome-diffusion-model-for-image-processing/ Abstract 图像恢复(IR)一直是低水平视觉领域不可或缺的一项具有挑战性的任务&…

[Go版]算法通关村第十三关黄金——数字数学问题之数论问题(最大公约数、素数、埃氏筛、丑数)

目录 题目&#xff1a;辗转相除法&#xff08;求最大公约数&#xff09;思路分析&#xff1a;辗转相除法&#xff08;也叫欧几里得算法&#xff09;gcd(a,b) gcd(b,a mod b)复杂度&#xff1a;时间复杂度 O ( n l o g ( m a x ) ) O(nlog(max)) O(nlog(max))、空间复杂度 O (…

基于自私羊群算法优化的BP神经网络(预测应用) - 附代码

基于自私羊群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于自私羊群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.自私羊群优化BP神经网络2.1 BP神经网络参数设置2.2 自私羊群算法应用 4.测试结果&#xff1a;5…

js案例:小球碰壁反弹

目录 一.效果预览图​编辑 解析 二.完整代码 代码讲解 html部分 js部分 一.效果预览图 解析 这个效果是为了以后&#xff08;过段时间会发的一个小游戏&#xff09;做js小游戏做准备的&#xff0c;基本结构是&#xff0c;定义两个div盒子&#xff0c;小盒子设置成圆球形状…

单片机(二)使用位移 让灯亮

一&#xff1a;硬件电路 P2 口&#xff1a; P2.0~ P2.7 是这些 I0 口 LED 阳极接 电源 &#xff0c; P20 口 为低电平 可以让 LED灯 亮 二&#xff1a;软件实现部分 两种 ① 通过循环 来展示从左 到右 #include "reg52.h"#define LED_PORT P2 // 定义单片机的P2端…

利用语义属性来进行时序知识图谱的补全

目录 摘要部分 张量分解 超平面投影 超平面 投影 超平面投影的应用 数学表示 正则化 引言部分 TKG嵌入方法 举例 相关工作 SKG嵌入方法 评判事实合理性的评分函数模型 平移模型 TransE TransE例子 张量分解模型 RESCAL 神经网络模型 TKG嵌入方法 外推 插…

queue ide is not exists in YARN

报错内容: 2023-08-17 17:30:31.342 [ERROR] [BaseTaskScheduler-Thread-7 ] o.a.l.o.s.a.AsyncExecTaskRunnerImpl (79) [run] - Failed to execute task astJob_1_codeExec_1 org.apache.linkis.orchestrator.ecm.exception.ECMPluginErrorException: errCode:…

【实训项目】“优教”APP设计

1.设计摘要 随着科技的发展和信息技术的日益普及,很多家长抱着望子成龙的心态,不遗余力的为孩子找合适的家教&#xff0c;而很多在校大学生也希望通过当家教增加一点经济收入,基于这一点家教服务平台将提供更好的管理系统,使家长更加了解学生,也通过这个平台使家教管理者对于大…

Spring Cloud Alibaba-Nacos Discovery--服务治理

1 服务治理介绍 先来思考一个问题 通过上一章的操作&#xff0c;我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址 &#xff08;ip&#xff0c;端口&#xff09;等硬编码到了代码中&#xff0c;这种做法存在许多问题&#xff1a; 一旦服务提供者地址变化&am…

如何将PC电脑变成web服务器:将内网主机映射到外网实现远程访问

如何将PC电脑变成web服务器&#xff1a;将内网主机映射到外网实现远程访问 我是艾西&#xff0c;今天跟大家分享内容还是比较多人问的一个问题&#xff1a;如何将PC电脑变成web服务器。内网主机作为web服务器&#xff0c;内容包括本地内网映射、多层内网映射解决方案、绕过电信…

[Linux]进程概念

[Linux]进程概念 文章目录 [Linux]进程概念进程的定义进程和程序的关系Linux下查看进程Linux下通过系统调用获取进程标示符Linux下通过系统调用创建进程-fork函数使用 进程的定义 进程是程序的一个执行实例&#xff0c;是担当分配系统资源&#xff08;CPU时间&#xff0c;内存…

android 重新签名bat

1.新建txt&#xff0c;修改后缀改为bat文件 sign.bat echo off:apk未签名文件名称 set apk_unsign"":apk签名文件名称 set apk_sign"":设置文件 set settingFileE:\apk\bat\sign\setting_sign.txt:读取settingFile第0行的 apk_unsign 值 for /f "to…

DETR-《End-to-End Object Detection with Transformers》论文精读笔记

DETR&#xff08;基于Transformer架构的目标检测方法开山之作&#xff09; End-to-End Object Detection with Transformers 参考&#xff1a;跟着李沐学AI-DETR 论文精读【论文精读】 摘要 在摘要部分作者&#xff0c;主要说明了如下几点&#xff1a; DETR是一个端到端&am…

Python入门之最基础1.0

记录学渣的学习过程 python入门学习1.0 前言一、 python学习之初需要注意的三个问题二、python所有内置函数的查看方式dir(__builtins__) 总结 前言 一、 python学习之初需要注意的三个问题 二、python所有内置函数的查看方式 dir(builtins) 总结 做自己的时候是发光的