当前位置: 首页 > article >正文

Java小白一文讲清Java中集合相关的知识点(四)

LinkedList底层结构

  • LinkedList底层实现了双向链表和双向队列特点
  • 可以添加任意元素,包括null,元素可以重复
  • 线程不安全,没有实现同步
LinkedList底层操作机制
  • LinkedList底层维护了一个双向链表
  • LinkedList中维护了两个属性first和last分别指向首结点和尾结点
  • 每个结点(Node 对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个结点,最终实现双向链表
  • 所以LinkedList的元素的增加和删除,不是通过数组完成的,相对来说效率较高
  • 模拟一个简单的双向链表
@SuppressWarnings({"all"})
public class Journey {
    public static void main(String[] args) {
        //模拟一个简单的双向链表
        Node life = new Node("life");
        Node is = new Node("is");
        Node journey = new Node("a journey");

        //各个结点相连,形成双向链表life is a journey
        life.next = is;
        is.next = journey;

        journey.pre = is;
        is.pre = life;

        //让first引用指向jack,其就是双向链表的头结点
        Node first = life;
        //让last引用指向journey,其就是双向链表的尾结点
        Node last = journey;

        //演示,从头到尾遍历链表
        while(true){
            if(first==null){
                break;
            }
            //输出first,
            System.out.println(first);
            first = first.next;
        }
        /**
         * 输出
         * Node name=life
         * Node name=is
         * Node name=a journey
         */
        System.out.println("==========演示链表添加数据==========");
        //演示下链表添加数据/对象是多么方便
        //要求:在 life 和  is 之间添加个 definitely
        //life definitely is a journey
        Node definitely = new Node("definitely");
        definitely.next = is;
        definitely.pre = life;
        life.next = definitely;
        is.pre = definitely;
        //让first指针重新指向头结点life
        first = life;
        while (true){
            if(first==null){
                break;
            }
            System.out.println(first);
            first = first.next;
        }
    }
}
//定义一个Node 类,表示双向链表的一个结点
class Node{
    public Object item;
    public Node next;
    public Node pre;
    public Node(Object name){
        this.item = name;
    }
    public String toString(){
        return "Node name="+item;
    }
}

在这里插入图片描述

LinkedList的增删改查

@SuppressWarnings({"all"})
public class Journey {
    public static void main(String[] args) {
        LinkedList list = new LinkedList();
        //添加结点,加了0 1 2
        for (int i = 0; i <= 2; i++) {
            list.add(i);
        }

        //演示删除结点
        System.out.println(list);//[0, 1, 2]
        list.remove();//删除了0
        //此时默认删除第一个元素0,输出此时的list空间状态
        System.out.println(list);//[1, 2]
        list.add("kerwin");
        System.out.println(list);//[1, 2, kerwin]
        list.remove(1);//删除1号索引元素,即2
        System.out.println(list);//[1, kerwin]

        //修改某个结点
        list.set(1,"fun");//修改1索引处的元素为fun
        System.out.println(list);//[1, fun]

        //得到某个结点对象
        list.add("Dopamine");
        System.out.println(list.get(2));//Dopamine
        System.out.println(list.getFirst());//1
        System.out.println(list.getLast());//Dopamine

        System.out.println("==================================================");
        //实现遍历--迭代器
        //因为LinkedList是实现了List的接口的,所以也可以使用迭代器遍历
        Iterator iterator = list.iterator();
        //要想起来快捷生成的方式哟, itit
        while(iterator.hasNext()){//这里要联想到迭代器的结构哈,先判断有没有下个元素
            Object obj = iterator.next();
            System.out.println(obj);

        }
        /**
         * 输出
         * 1
         * fun
         * Dopamine
         */

        //实现遍历--增强for
        for(Object o: list){
            System.out.println("els->"+o);
        }
        /**
         * 输出
         * els->1
         * els->fun
         * els->Dopamine
         */

        //实现遍历--最normal的for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.println("normal-For->"+list.get(i));
        }
        /**
         *输出
         * normal-For->1
         * normal-For->fun
         * normal-For->Dopamine
         */


    }
}
对应的各个方法的源码剖析
//增方法的源码剖析   add
    
//深入LinkedList的add方法,可知
//传入一个数进去后,先自动装箱,然后,执行add方法,源码如下:
        public boolean add(E e) {
        linkLast(e);
        return true;
    }
//继续深入linkLast方法,可得知其内部具体实现,先了解下Node类的具体内容,再往下看linkLast函数
    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;
        }
    }

    void linkLast(E e) {
        final Node<E> l = last;//起初last为null
        final Node<E> newNode = new Node<>(l, e, null);//创建一个新结点,
        //prev为null,element就是此时的元素,next也置为null
        last = newNode;//laast指向新建结点
        if (l == null)
            first = newNode;//如果首结点为空的话,first就指向新节点,使之为首结点
        else
            l.next = newNode;//否则首结点之后才紧跟新建结点
        size++;
        modCount++;
    }


在这里插入图片描述

这时候,你会发现,加了两个元素之后,first指向第一个结点,而last指向第二个结点,这不正合心意??

//删方法的源码剖析   remove
    public E remove() {
        return removeFirst();
    }

    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)//如果为空,还删啥?抛异常lou
            throw new NoSuchElementException();
        return unlinkFirst(f);//不为空,则开始操作
    }

    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;//将第一个元素置空
        f.next = null; // help GC
        first = next;//first移动到原先头结点的next,也就是移动到了原先的第二个结点处
        if (next == null)//假如原先只有一个结点,那么next==null,
            //也就谈不上断掉下一个结点指向自己的prev了
            last = null;
        else
            next.prev = null;//倘若原先的结点不止一个,
        //那么就需要把第二个结点指向自己的prev也断开
        size--;
        modCount++;
        return element;
    }


在这里插入图片描述

ArrayList和LinkedList比较

在这里插入图片描述

如何选择ArrayList和LinkedList?

  • 如果我们改查的操作多,选择ArrayList
  • 如果我们增删的操作多,选择LinkedList
  • 一般来说,程序中,大部分是查询,因此大多数情况下,会选择ArrayList
  • 在一个项目中,根据业务灵活选择,也可能这样,有可能一个模块用的是ArrayList,另一个模块使用的是LinkedList

http://www.kler.cn/a/290977.html

相关文章:

  • linux springboot项目启动端口被占用 Port 8901 was already in use.
  • 内核执行时动态的vmlinux的反汇编解析方法及static_branch_likely机制
  • BERT的改进:ModernBERT
  • windows下搭建本地sofa-registry
  • StarRocks:存算一体模式部署
  • 练习题 最小栈
  • LEAP模型在能源环境发展、碳排放建模预测及分析中实践应用
  • Python面向对象(15对象嵌套特殊成员)
  • 云原生 | 在 Kubernetes 中使用 Cilium 替代 Calico 网络插件实践指南!
  • 大零售时代:开源 AI 智能名片、2+1 链动与 O2O 商城小程序引领融合新趋势
  • Ajax 2024/3/31
  • 零售自动化新趋势:AI 智能名片与 S2B2C 商城系统助力零售业变革
  • git常用之已存在的目录转换为一个 GIT 项目并托管到github仓库
  • 每天五分钟深度学习:广播机制(以python语言为例)
  • 【大数据】生活中三大数据的概念及其关系
  • 新品上市丨科学级新款制冷相机sM4040A/sM4040B
  • 【ShuQiHere】深入理解递归:从基础概念到实际应用
  • ffmpeg音视频开发从入门到精通——ffmpeg日志及目录操作
  • Java开发笔记--通用消息组件设计(移动短信、华为短信、163邮件)
  • chapter03 流程语句 知识点Note
  • JS基础-ClassList -移动端插件的引入-touch事件-sessionStorage 和 localStorage
  • STM32—I2C的基本时序,MU6050的ID读取
  • 云计算和传统IT相比,有哪些优势?
  • map和set的区别和底层实现是什么?map取值的 find,[],at方法的区别
  • GitLab 是什么?GitLab使用常见问题解答
  • 论文浅尝 | TaxoLLaMA: 用基于WordNet的模型来解决多个词汇语义任务(ACL2024)