Java-集合(3)

news2024/11/19 10:41:59

Vector集合类

1.Vector底层保存数据的也是一个对象数组:protected Object[] elementDate;
2.Vector是线程同步的,也就是线程安全Vactor的操作方法都带有synchronized修饰。以此可以进行安全线程保障,所以在开发中如果确认只有一个线程操作集合就用ArrayList如果有多个线程就用Vector

Vector和ArrayList的比较

在这里插入图片描述

Vector创建和扩容底层源码分析

public class Vector_ {
    public static void main(String[] args) {
        Vector vector = new Vector();
        for (int i = 0; i < 10; i++) {
            vector.add(i);
        }
        vector.add(10);
    }
}

总体上来说Vector和ArrayList的源码结构差不太多,主要是扩容的倍数不太一样,和默认赋值的地方不一样,Arraylist是先赋0空间,然后add的时候再赋10个空间。而Vector是一开始的无参构造就直接赋10个空间

1.进入无参构造,可以看到无参构造直接传入 10 调用另一个有参构造,最终创建了一个10空间大小的elementDate数组
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.接着开始进入for循环进线添加,同样的先自动装箱
在这里插入图片描述
3.进入add方法,修改次数+1,然后使用ensureCapacityHelper方法用于确认如果要添加1个,剩余空间是否够,如果不够就扩容,够的话就直接返回进行下面的添加。elementCount的作用和ArrayList的size是一样的,可以理解为一个数组的指针,初始指向数组的第0个空间。随着不断的add,elementCount也会逐渐增加指向第1/2/3/4/5…
在这里插入图片描述
4. 由于Vector无参构造直接赋了10个空间所以初次add是会直接返回进行添加的,所以我们直接跳到第11次添加,需要扩容的时候:因为前面进行了10次添加,所以此时elementCount是10,代表指向数组的最后一个空间,接着调用ensureCapacityHelper方法用于确认再添加一个元素,剩余空间是否还够用。传入了elementCount + 1,也就是11。

在这里插入图片描述
5.进入ensureCapacityHelper可以看到,是先用if判断 minCapacity(需要的空间11)减去 elementDate.length(当前的空间10),11-10大于0 所以进入grow扩容方法
在这里插入图片描述
6.进入grow扩容方法且把需要空间数量传入,首先将elementDate当前的空间数量给oldCapacity老容量
然后用老容量+(这里用了三元运算,capacityIncrement是否大于0,如果大于0就返回capacityIncrement,如果小于等于0就返回老容量)而capacityIncrement只要不修改就默认是0 ,所以这里会返回老容量,也就意味着newCapacity新容量是老容量的2倍。
然后进行一个if判断 新容量减去需要的容量是否小于0,如果小于0就意味着新容量不够需要的容量,那么就会把需要的容量的值赋给新容量。此时新容量是20 而需要的容量是11,所以直接进入下方的Arrays.copyOf进行扩容。
在这里插入图片描述
7.扩容完毕后直接原路返回到add进行添加操作。可以看到此时elementDate的空间已经被扩容成20个了,而添加使用的是elementCount++,由于是后++所以是先添加再将elementCount往后移一位。
在这里插入图片描述

LinkedList集合类

  1. LinkedList底层实现了双向链表和双端队列特点
  2. 可以添加任意元素(元素可以重复),包括null
  3. 线程不安全,没有实现同步

LinkedList底层结构

  1. LinkedList底层维护了一个双向链表(待会简单讲解)
  2. LinkedList中维护了两个属性first和last分别指向 首节点和尾节点
  3. 每个节点(Node对象),里面又维护了prev(指向前一个),next(指向后一个),item(保存数据的)三个属性,其中通过prev指向前一个Node对象,通过next指向后一个Node对象,最终实现双向链表
  4. 所以LinkedList的元素的添加和删除不是通过数组完成的,相对来说效率较高

简单了解双向链表

下面通过简单代表来初步认识什么是双向链表,以及怎么对双向链表进行添加和删除

简单的来说双向链表就是通过Node类的prev和next来连接多个Node对象,然后通过多态的特点来遍历

双向链表的连接

代码演示:

public class LinkedList {
    public static void main(String[] args) {
        //首先创建三个Node对象
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node mary = new Node("mary");
        //此时这三个对象彼此之间没有任何联系,是独立存在的。
        //下面我们用prev和next让他们彼此之间联系起来
        jack.next = tom;//jack的下一个指向tom
        tom.next = mary;//tom的下一个指向mary
        mary.prev = tom;
        tom.prev = jack;
        //接着创建first和last分别指向这个链表的头和尾
        Node first = jack;
        Node last = mary;
    }
}
class Node{
    public Object item ;
    public Node prev;
    public Node next;

    public Node(Object item) {
        this.item = item;
    }

    @Override
    public String toString() {
        return "name = "+item;
    }
}

可以看到此时三个Node对象彼此之间就有了联系,关系如图:
在这里插入图片描述

双向链表的连接

在上面我们通过prev和next将三个对象连接起来了,那么下面将使用first和last将这个链表遍历
从头开始遍历:

while (true){
            if (first == null){//如果first指向null就退出循环
                break;
            }
            System.out.println(first);
            first = first.next;
        }

因为first先开始是指向第一个对象:jack,所以不是null,接着输出first,那么根据多态的特性,此时first的编译类型是Node first这个引用,那么实际的运行类似其实是Node jack,再自动使用toString方法输出jack的item。
接下来将first.next赋给first。这里的first.next就是Node.tom,所以此时first就变成了Node tom
接着又进入while循环,first依旧不等于null,所以输出 tom
而后又进行 first = first.next ,此时的first是Node.tom,而tom的next又指向mary,所以此时first又变成了mary。接着又进入循环输出mary。
然后又运行first = first.next,此时的first是Node mary,而mary的next是null,所以first变成了null。再下一次循环就会退出、

那么根据上面的规律要从尾往前遍历,只需将first换成last,且将next换成prev即可。

while(true){
            if (last == null){
                break;
            }
            System.out.println(last);
            last = last.prev;
        }

遍历图示:
在这里插入图片描述

总而言之:双向链表的遍历,就是通过一直改变first或者last指向的地址进行对不同对象的调用。

双向链表的增加与删除

通过上面的了解双向链表之间的链接是通过next和prev,那么同理当我们需要将链表当中的某一个对象删除或者增加某一个对象时,也只需改变前一个和后一个的next和prev的指向即可。
代码演示

//创建一个新的对象
        Node smith = new Node("smith");
        //将前一个的next的指向smith,且将smith的next指向下一个
        tom.next = smith;
        smith.next = mary;
        //将后一个的prev也指向smith,且将smith的prev指向上一个
        mary.prev = smith;
        smith.prev = tom;

遍历输出确认一下:
name = jack
name = tom
name = smith
name = mary
可以看到确实被添加进去了
图示体现链表添加
在这里插入图片描述
双向链表的删除
同理,将一个对象从双向链表删除也是改变要删除对象的前后next和prev的指向即可
例如将smith从双向链表中删除

//将前一个元素的next跳过smith直接指向后一个,且将后一个的prev跳过smith直接指向前一个
        tom.next = mary;
        mary.prev = tom;

这样就从链表中删除了smith,对于smith的next和prev的指向可以改也可以不改,因为在使用链表的时候应该不会到smith那去了。
图示:
在这里插入图片描述

LinkedList添加删除底层源码分析

根据上面的学习我们知道LinkedList底层是使用了双向链表来保存数据的,那么下面就根据一段代码来追一下源码具体是怎么添加的。

public class LinkedList_2 {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add(1);
        linkedList.add(2);
    }
}
  1. 首先进入无参构造,做一个初始化。里面维护了几个变量 {size:链表的长度,first:首节点 ,last:尾节点,modCount:修改次数}
    在这里插入图片描述
    在这里插入图片描述
  2. 同样的做一个自动装箱
    在这里插入图片描述
  3. 进入add方法,调用linkLast(e)方法传入要添加的元素
    在这里插入图片描述
  4. 进入重点方法:linkList:

首先将last尾节点指向的元素备份一个到Node l
然后新建一个结点newNode,使用的是有参构造,此有参构造是Node(Node prev,Object item,Node next),代入参数就是将last指向的对象给newNode这个节点的prev(上一个),将此次要存的元素给item,将null给next。因为此add方法是在最后面添加一个节点,所以新节点的next肯定是null。
接着将last指向新节点,原理就是在最后添加,那么last就是指向最后一个节点的
然后进入一个if判断:如果L是指向空,这里的L是last在被重新指向的指向,可以理解为是否是第一次添加节点。如果是第一次添加就将first指向这个新节点,因为是第一次添加节点,所以first和last都是需要指向这个节点的。如果不是第一次添加节点,就让上一个节点的next指向这个新节点,l 就代表last上一个的指向也就是添加之前的最后一个节点。
最后size长度+1,修改次数+1.,返回到add执行完毕
在这里插入图片描述

  1. 上面追完了第一次添加结点,下面接着追一下第2次添加节点。为了更好理解,下面图示一下添加完第一个节点的双向链表
    在这里插入图片描述

  2. 下面开始追添加第二个节点,要将一个节点添加进行,需要将last指向要添加的节点,然后将前一个节点的next也指向要添加的节点,最后将要添加的节点的prev指向上一个节点。那么看看源码是怎么操作的

  3. 首先还是自动装箱,接着进入add方法,调用linkLast传入此次要添加的元素 2
    在这里插入图片描述

  4. 进入linkList,

首先将last的指向备份一个给l,此时的last和L都指向第一个节点
然后创建一个新节点:将l传入新节点的prev,将此次添加的元素保存到新节点的item,将新节点的next指向null
然后让last不指向第一个节点转而指向这次添加的节点
接着进入if判断是否是第一次添加节点,因为是第二次添加节点所以l不指向null,进入l.next = newNode,注意这里的l还是指向第一个节点,那么这里也就意味着将第一个节点的next指向第二个新节点
最后第二个节点就添加好了,size长度+1,修改次数+1.后面再要添加节点也是根据相同的结构添加

在这里插入图片描述
9. 添加好的双向链接如图
在这里插入图片描述
LinkedList的删除
LinkedList里重载了很多删除方法,针对不同的需要可以使用不同的删除方法
在这里插入图片描述
下面使用最普通的删除方法追源码分析下,同增加的原理,删除就是需要将双向链表的前后节点的next和prev改变下,如果删除的是头尾还需要将first和last改变下。

public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add(1);
        linkedList.add(2);
        linkedList.add(3);
        linkedList.remove();
        System.out.println(linkedList);
    }

上面添加了3个元素,此时的双向链表中有3个节点。下面分析下此删除方法会删除哪一个节点,且是怎么删除的

  1. 可以看到普通的remove方法底层使用的是removeFirst方法,根据方法名可以推断出应该是first指向那个就删除哪个
    在这里插入图片描述
  2. 进入removeFirst方法,首先将first指向的节点 备份给 f ,然后进入if判断当前的链表是否是空链表,如果是就抛出异常,如果不是就调用unlinkFirst方法且传入 f
    在这里插入图片描述
  3. 进入unlinkFirst方法 这里是实际删除的地方 提示:此时 f 代表第一个节点

首先将 f 指向的节点保存的元素给到 element
然后将 f 指向的节点的next也就是下一个节点备份给next 此时Node next指向第二个节点
接着让 f 指向的节点的 item和next置空 让后面GC回收掉
然后将next(第二个节点)代替第一个节点,让first指向它
接着进入一个if 判断 看看此链表是不是一共只有要删除的这一个节点,如果是就干脆将last置空
如果不仅有一个节点,就将第二个节点的prev置空(因为他的上一个要被删除了)。
最后链表的size-1 修改次数+1,返回删除节点保存的元素,删除执行完毕
在这里插入图片描述
总的来说LinkedList的增删改查底层还是对于双向链表的改动,所以需要了解双向链表


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

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

相关文章

dubbo学习笔记3(小d课堂)

dubbo高级特性 启动依赖检查 我们现在直接来启动我们的消费者&#xff1a; 它会报错。 我们 再去直接运行我们的消费者就不会报错。 我们也可以不在代码中去配置&#xff1a; 实际工作中比较建议使用这种方式。 dubbo.reference.check是配置所有的reference里的service都是f…

C语言:大小端

大小端 对于整型来说&#xff0c;内存中存放的是补码&#xff0c;补码是按大小端模式进行存储&#xff1a; 大端存储数据低位存在内存高地址&#xff0c;高位存在内存低地址小端存储数据低位存在内存低地址&#xff0c;高位存在内存高地址 记忆方法 小小小&#xff1a;数据 小…

前端组件库自定义主题切换探索-01

探索原因背景 首先自然是项目有需求&#xff0c;这是必须去做的原因 其次&#xff0c;是我们项目没有直接使用市面上现成的基于element-ui或者ant-design的第三方UI框架&#xff0c;比如avue&#xff0c;而是有着自己的UI组件库 第三&#xff0c;我们的组件库基于ant-design-v…

Win10下干净卸载VMware15.5

一、说明 虚拟机属于服务软件&#xff0c;在Windows10下卸载实属不易。下面请看我所总结的卸载文档。 二、如何彻底卸载VMware虚拟机 需要删除的部分很多&#xff0c;包括&#xff1a; 三、删除步骤一&#xff1a;需要禁用VM虚拟机服务 首先&#xff0c;因为VM的软件属于底层…

【自学Python】Python转义字符

文章来源嗨客网&#xff08;www.haicoder.net&#xff09; Python转义字符 Python转义字符教程 在编程中有一些字符是打不出来的&#xff0c;比如换行&#xff0c;提示音&#xff0c;下一个制表位等等&#xff0c;于是程序语言的开发者就设计了转义序列&#xff08;escape se…

4 机器学习之决策树

学习笔记自&#xff0c;慕课网 《Python3 入门人工智能》 https://https://coding.imooc.com/lesson/418.html#mid32776 决策树、异常检测、主成分分析 常用的分类方法&#xff1a; 逻辑回归的思路&#xff1a; 决策树的思路&#xff1a; 1. 决策树 1.1 ID3决策树&#x…

Java 防御XSS攻击实战与示例代码

XSS, 跨站脚本攻击, 简单来说, 就是非本站点的脚本被执行了。 关于XSS 的详细介绍和防御参考: XSS(跨站脚本)攻击与预防 和 跨站脚本攻击(XSS)及防范措施。 本篇介绍在Java 项目中如何快速修复XSS 漏洞。本篇使用的是黑名单的方式, 对于非法字符进行转义。 黑名单的方式…

Win10的IE以及其他浏览器无法使用的解决方法

前言 最近&#xff0c;突然无法使用Win10的IE&#xff0c;网络连接正常&#xff0c;重置过IE&#xff0c;恢复过高级选项设置&#xff0c;检查过无代理设置&#xff0c;清理过缓存删除IE个人设置&#xff0c;以及清理过DNS解析缓存&#xff0c;IE都会显示无法访问此页面的网络…

Django3还可以这样学之Django的创建

Django (Flask说&#xff1a;“其实我是你的双胞胎哥哥啊”) 一、前言 传统的web框架为MVC设计模式&#xff0c;即M是指业务模型&#xff0c;V是指用户界面&#xff0c;C则是控制器。虽然这种设计模式耦合性低、可重用性高、部署快等特点&#xff0c; 但是完全理解该设计模式…

【机器学习】贝叶斯算法详解 + 公式推导 + 垃圾邮件过滤实战 + Python代码实现

文章目录一、贝叶斯简介二、贝叶斯公式推导三、拼写纠正案例四、垃圾邮件过滤案例4.1 问题描述4.2 朴素贝叶斯引入五、基于朴素贝叶斯的垃圾邮件过滤实战5.1 导入相关库5.2 邮件数据读取5.3 构建语料表&#xff08;字典&#xff09;5.4 构建训练集的特征向量5.5 朴素贝叶斯算法…

web服务器的相关配置

一&#xff1a;简单介绍web服务 1、什么是www www是world wide web的缩写&#xff0c;也就是全球信息广播的意思。通常说的上网就是使用www来查询用户所需要的信息。www可以结合文字、图形、影像以及声音等多媒体&#xff0c;并通过可以让鼠标单击超链接的方式将信息以Interne…

数字化时代,企业如何培养员工的数据思维意识

很多的企业&#xff0c;他们都有意识想通过大数据或者商业智能BI来推动一些数据信息化的项目建设&#xff0c;比如企业的财务分析、经营管理分析、供应链分析、运营、市场分析等等&#xff0c;这都是很好的表现。 但在实际跟进和深入沟通的时候就发现很多分析的想法是没有办法…

OpenCV(9)-图像轮廓

图像轮廓 图像轮廓概述 图像轮廓是具有相同颜色或强度的连续点的曲线 作用&#xff1a; 可以用于图形分析物体的识别与检测 注意&#xff1a; 为了检测的准确性&#xff0c;需要先对图像进行二值化或Canny操作画轮廓时会修改输入的图像 轮廓查找API&#xff1a; findConto…

FastDFS简介/架构/安装/代码

1简介/架构 1.1简单介绍 分布式文件系统&#xff0c;看名字就知道&#xff0c;它可以存储在不同的机器上。肯定也要有路由功能。 特点 备份容错&#xff0c;负载均衡&#xff0c;动态扩容 场景 0-500MB之间大小的文件存储&#xff08;图片&#xff0c;短视频&#xff0c;文…

LeetCode[703]数据流中的第K大元素

难度&#xff1a;简单题目&#xff1a;设计一个找到数据流中第 k大元素的类&#xff08;class&#xff09;。注意是排序后的第 k大元素&#xff0c;不是第 k个不同的元素。描述&#xff1a;请实现 KthLargest类&#xff1a;KthLargest(int k, int[] nums) 使用整数 k 和整数流 …

【北邮果园大三上】数据挖掘

数据挖掘 大数据 定义&#xff1a; 体积时效性种类值 数据挖掘模型 1.归纳已知 2.预测未来 1. 数据的质量处理和度量方法 1.1数据 1.1.1数据属性 属性类型: 标称(nominal)序数(ordinal)区间(interval)比率(ratio) 非对称的属性(asymmetric attribute): 对于非对称的…

airserver怎么下载使用?IOS设备无线传送到Mac电脑的屏幕上教程

AirSever是一种通用的镜像接收器&#xff0c;非常适合镜像iPhone或Android视频文件。 它可以将简单的大屏幕或投影仪变成通用的屏幕镜像接收器。 使用启用了AirSever的设备&#xff0c;您可以用户拥有iPhone&#xff0c;iPad&#xff0c;Mac&#xff0c;Android&#xff0c;Nex…

Proxy详解

Proxy Proxy(代理&#xff09;&#xff0c;首先理解代理是什么意思&#xff0c;才能更便于了解Proxy的作用。 Proxy是一个代理&#xff0c;可以这么理解&#xff0c;就相当于它是一个快递代理点&#xff0c;快递会运输到该代理点&#xff0c;我们取件只需要去对应的代理点取件…

2023年宝塔面板快速部署Rocket.Chat,安卓,PC,ios端使用

官方网站快速部署代码:Docker & Docker Compose - Rocket.Chat Docs 环境:centos 服务器必须大于2核2G,否则docker占用资源太多会起不起来 安装宝塔面板 一 . 宝塔面板安装docker 当然也可以手动安装:如果您还没有&#xff0c;请确保您已安装并运行Docker和Docker-comp…

Docker 数据卷

问题&#xff1a;通过镜像创建一个容器。容器一旦被销毁&#xff0c;则容器内的数据将一并被删除。但有些情况下&#xff0c;通过服务器上传的图片出会丢失。容器中的数据不是持久化状态的。 那有没有一种独立于容器、提供持久化并能服务于多个容器的东西呢&#xff1f; 什么是…