【常见集合】Java 常见集合重点解析

Java 常见集合重点解析

1. 什么是算法时间复杂度?

在这里插入图片描述

  • 时间复杂度表示了算法的 执行时间数据规模 之间的增长关系;

什么是算法的空间复杂度?

  • 表示了算法占用的额外 存储空间数据规模 之间的增长关系;

常见的复杂度:

在这里插入图片描述

2. ArrayList 底层的数据结构是什么?

推荐博客

ArrayList 的底层是动态数组(Array),是一种用 连续的内存空间 存储 相同数据类型 数据的现象数据结构(单列),并且会随着数据的增加而扩容;

数组下标为什么从 0 开始?

  • 数组通过下标访问的原理就是通过数组的首元素地址与下标去计算对应下标元素的地址,从而访问对应的元素;
  • 寻址公式:baseAddress + i * dataTypeSize,计算下标的内存地址效率较高;
  • 如果是从 1 开始:寻址公式则为 baseAddress + (i - 1) * dataTypeSize,这样计算下标的时候还多了一步减法;
    • 而这个高频的操作,“日积月累”,如 几亿次的操作,这个减法也挺消耗 CPU 资源的,要尽可能的提高效率;

查找的时间复杂度?

  • 随机查询(通过下标)的时间复杂度是 O(1);
  • 查找元素(未知下标的值查询)的时间复杂度是 O(n);
  • 查找元素(未知下标但是已排序)通过二分的时间复杂度是 O(logn);

插入、修改、删除的时间复杂度?

  • 修改的时候,通过下标访问并修改即可,所以是O(1);
  • 按下标插入和删除的时候,为了保证数组的内存连续性,需要挪动数组元素,平均时间复杂度是 O(n);
    • 插入的时候甚至可能会扩容:O(n);

3. ArrayList 源码(底层原理)有了解过吗?

构造方法:

在这里插入图片描述

扩容机制:

  1. 空集合但是容量为0,即空数组;
    • 第一次添加元素的时候,如果 需要的空间大小 minCapacity 小于默认容量,则扩容至默认容量;
  2. 空集合但是容量不为0,即数组元素都为空;
    • 第一次添加元素的时候,与默认容量无关,按正常的情况走;
  • 添加之前要判断本此次添加 需要的空间大小 minCapacity 是否小于当前的容量,是则无需扩容,否则需要扩容;

    • 原数组扩容为原来的 1.5 倍(如果还是达不到 minCapacity,则取 minCapacity);

    • 如果扩容后的空间大于最大的数组大小,则调用一个方法计算

      在这里插入图片描述

      private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
      

      根据 minCapacity 计算出一个合理的巨大容量;

    • 然后 elementData[size++] = e,在有效数组尾添加节点;

在这里插入图片描述

ArrayList list = new ArrayList(10) 中 ArrayList 扩容了几次?

  • 0 次,调用构造方法,只是实例了一个指定容量的 ArrayList,未扩容;

4. 如何实现数组和 List 之间的转化?

在这里插入图片描述

  1. 数组 => List

使用 JDK 中 Arrays 数组工具类的 asList 方法(可变参数),将其转化为 List;

在这里插入图片描述

不难看出,如果修改原数组指向的元素,是会影响 List 的,当然,触发了扩容之后 List 就换了个数组,就不受影响;

如果要不受影响的话,就用遍历或者 stream 流去转化 List;

  1. List => 数组

使用 List 对象的 toArray 方法转化为数组:

  • 无参的返回的是 Object 数组;

在这里插入图片描述

  • 有参的则是指定类型的数组,并且如果数组足够大,数据会拷贝到数组里,反正以返回值为主就行了;

在这里插入图片描述

不难看出,每次转化数组都是拷贝一份 List 中的数据,所以修改 List 中的数据不会影响转化后的数组;

5. 链表操作数据的时间复杂度是多少?

推荐博客

单向链表只有一个方向(只有头节点),每个节点有一个后继节点 next;

在这里插入图片描述

  • 头插法,O(1)
  • 指定节点的后面插入,O(1)
  • 指定节点的前面插入,O(n)
  • 指定节点的删除,O(n)
  • 指定下标的增删查改,O(n)
  • 查找指定值,O(n)

双向链表则有两个方向(有头节点和尾节点),每个节点有一个后继节点 next 还有一个 前驱节点 prev;

  • 支持双向遍历,提高灵活性;

在这里插入图片描述

  • 头插法、尾插法,O(1)
  • 指定节点的前面/后面插入,指定节点的删除,O(1)
  • 指定下标的增删查改,O(n)
  • 查找指定值,O(n)

在这里插入图片描述

6. ArrayList 和 LinkedList 的区别是什么?

6.1 底层数据结构

ArrayList 是动态数组,LinkedList 是双写链表;

6.2 操作数据效率

  1. 下标访问

ArrayList 按照下标查询的时间复杂度是 O(1),LinkedList 则不支持下标查询,如果从逻辑上的下标查询的话,则时间复杂度是 O(n);

  1. 值查询

ArrayList 和 LinkedList 都是 O(n);

  1. 新增/删除

ArrayList 从尾部插入/删除是 O(1),但是其他部分增删需要挪动数组,甚至触发扩容,时间复杂度是 O(n);

  • 只有给定下标的 O(n);

LinkedList 从头尾节点插入都是 O(1),其他都需要遍历链表,没有扩容的情况,时间复杂度为 O(n);

  • 给定下标是需要遍历的 O(n),但是给定节点则不需要遍历的 O(1);

6.3 内存空间占用

ArrayList 底层是数组,内存连续,节省空间;

LinkedList 底层是双向链表,除了 value 还存了两个指针,更占用内存;

6.4 线程安全

ArrayList 和 LinkedList 都不是线程安全的;

要保证线程安全两种方案:

  1. 在方法内使用,局部变量是线程安全的(让多线程不同时修改同一个对象);

  2. 套壳加锁返回线程安全的 List 实例;

    在这里插入图片描述

7. 什么是二叉树?什么是二叉搜索树?什么是红黑树?

二叉树

  • 二叉搜索树(Binary Search Tree,BST),又叫二叉查找树,有序二叉树;

  • 在树中的任意一个节点,其左子树中的每个节点都小于这个节点,右子树则都大于;

  • 没有键相等的节点;

  • 通常情况下二叉搜索树的增删查改,时间复杂度是 O(logn);

    恶劣情况的二叉搜索树会退化成链表(时间复杂度退化为 O(n)):

    在这里插入图片描述

而我们需要的是平衡度高的二叉搜索树,才能保证查找效率,AVL树和红黑树都是自平衡二叉搜索树。

AVL树通过旋转操作来保持平衡,并且在平衡性上有更严格的要求。

红黑树则使用颜色标记和旋转操作来维护平衡(维持一些规则),相对而言更灵活一些。

在AVL树中,任意节点的左右子树的高度差不超过1,而红黑树的规则则是:

在这里插入图片描述

  • 红黑树(Red Black Tree):也是一种自平衡的二叉搜索树;
  • 所有的红黑规则都是希望红黑树能够保证平;
  • 红黑树的时间复杂度:增删查改都是 O(logn);
    • 旋转调整是 O(1);

8. 什么是散列表(哈希表)?

推荐博客

什么是散列表?

  • 散列表(Hash Table)又名哈希表 / Hash 表;
  • 根据键直接访问内存存储位置的数据结构;
  • 由数组演化而来,利用了数组支持按照下标进行随机访问数据的性质;
  • 数组的每个位置不一定都有值,存储的比较松散,一般会保留一定的空位置;

在这里插入图片描述

散列冲突是什么?

  • 散列冲突又叫哈希冲突、哈希碰撞;
  • 指的是多个 key 映射到同一个数组的下标位置;

散列冲突其实无法完全避免,即使是 md5 这样的算法,也无法绝对的让 key 和 hashValue 一对一,更何况我们没有那么大的数组来存;

  • 要预防或者减少哈希冲突,可以用一些均匀化的算法,让哈希冲突不要集中在某个下标,适当扩充数组…

散列冲突,如何数组同个下标的存储数据?

  • 开散列的方式:
    • 数组的每个下标称之为一个哈希桶(bucket)或者哈希槽(slot);
    • 每个哈希桶会对应一个链表/一颗红黑树;
    • 散列冲突后的元素都放到对应哈希桶内的链表/红黑树中;

9. 说一下 HashMap 的实现原理?

  • 底层使用 Hash 表数据结构,即数组 + 链表/红黑树;
  • 添加数据时,计算 key 的值确定元素在数组中的下标;
    • key 相同则替换;
    • 不同则存入链表或红黑树;
  • 获取数据通过 key 的 hash 计算下标,查询链表/红黑树下标元素,一般是可以看成是 O(1) 的;

HashMap 的 jdk1.7 和 jdk1.8 的区别:

  • jdk1.8 之前,数组 + 链表(头插);
  • jdk 1.8 及之后,数组 + 链表(尾插) + 红黑树,链表长度和数组长度各达到一定值(链表长于 8 并且数组长于 64)则会从链表转化尾红黑树;

10. HashMap 的 put 方法的具体流程?

在这里插入图片描述

哈希表被无参实例化的时候,数组没有被实例化,第一次 put 的时候,再 resize 为 16;
(因为可以由其他构造方法去构造 HashMap,所以第一次 put 时数组不为 null,可能数组长度不会以 16 开始,但是数组的长度一定是 2 的倍数)

在这里插入图片描述

  1. 判断键值对数组 table 是否为空或为 null,否则执行 resize() 进行扩容(初始化)

  2. 根据键值 key 计算 hash 值得到数组索引

  3. 判断 table[i] == null,条件成立,直接新建节点添加

  4. 如果 table[i] == null,不成立

    4.1 判断 table[i] 的首个元素是否和 key 一样,如果相同直接覆盖 value

    4.2 判断 table[i] 是否为 treeNode,即 table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对

    4.3 遍历 table[i],链表的尾部插入数据,然后判断链表长度是否大于 8,大于 8 的话把链表转换为红黑树,在红黑树中执行插入操 作,遍历过程中若发现 key 已经存在直接覆盖 value

  5. 插入成功后,判断实际存在的键值对数量 size 是否超多了最大容量 threshold(数组长度* 0.75),如果超过,进行扩容。

11. HashMap 的扩容机制?

在这里插入图片描述

参考回答:

  • 在添加元素或初始化的时候需要调用 resize 方法进行扩容,第一次添加数据初始化数组长度为 16,以后每次每次扩容都是达到了扩容阈值(数组长度 * 0.75)

  • 每次扩容的时候,都是扩容之前容量的 2 倍;

  • 扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新的数组中

    • 没有hash冲突的节点,则直接使用 e.hash & (newCap - 1) 计算新数组的索引位置
    • 如果是红黑树,走红黑树的添加
    • 如果是链表,则需要遍历链表,可能需要拆分链表,判断 (e.hash & oldCap) 是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上

在这里插入图片描述

12 HashMap 的寻址方式?

在这里插入图片描述

(h = key.hashCode()) ^ (h >>> 16) ,称为 二次哈希

  1. 首先获取 key 的 hashCode 值;
  2. 然后右移 16 位异或运算原来的 hashCode 值
  • 主要作用就是使原来的 hash 值更加均匀,减少 hash 冲突,例如 17 或者 33 模 16 都等于 1,二次哈希后再取模大概率就不哈希冲突了;

(n - 1) & hash,代替取模,性能更好一些,n 即数组长度,必须是 2 的整数倍才等价于取模;

  • 例如 16 => 15 的二进制 1111,按位与就是保留后面四个比特位,就是取模;

为何 HashMap 的数组长度一定是 2 的整数倍?

  1. 计算索引时效率更高:(n - 1) & hash,代替取模,n 即数组长度,必须是 2 的整数倍才等价于取模;
  2. 扩容时重新计算索引效率更高:hash & oldCap == 0 的元素留在原地,否则 newPos = oldPos + oldCap
    • 例如 16 => 32:
    • 两次计算索引的结果一样 <=> hash 的后 4 位 和 后 5 位 相等 <=> 那么就是从右到左第 5 位必须是 0
      • 也就是 10000(即原数组长度) 按位与 hash,得知其是否为 0;
    • 所以,hash & oldCap == 0,并且在**数组为 2 的整数倍的情况下**是可以推理出来两次计算索引的结果一样(充要条件);

13. HashMap 在 jdk1.7 下,多线程死循环问题

单线程情况下:

在这里插入图片描述

多线程的情况下(两个线程同时让一个 HashMap 扩容):

在这里插入图片描述

线程 1 完成扩容:

在这里插入图片描述

线程 2 执行:

  • 现在 cur2 指向 3,会将 3 头插到扩容数组中,由于这里节点 3 跟线程 1 扩容后的节点 3 是同一个,所以就会出现这种情况:

在这里插入图片描述

  • 所以这样就会导致成环不断循环下去!

问题原因:

  1. 头插法,导致插入的顺序和原来链表的顺序相反的;
  2. table 是共享的,table 里面的元素也是共享的,while 循环都直接修改 table 里面的元素的 next 指向,导致指向混乱;

而 jdk 8 扩容算法做出了调整,使用了尾插法还有红黑树;还可以用线程安全的 ConcurrentHashMap;

  • 其中还有 高低位拆分转移方式,可以查询资料去了解,在这里不讨论;

14. HashSet 有了解过吗?

  1. HashSet 实现了 Set 接口,仅存储对象;
  2. HashMap 实现了 Map 接口,存储的是键值对;
  3. HashSet 底层其实是用 HashMap 实现存储的;HashSet 封装了一系列 HashMap 的方法;依靠 HashMap 来存储元素值(利用 hashMap 的 key 键进行存储), 而 value 值默认为 Object 对象;
  4. 所以 HashSet 也不允许出现重复值,判断标准和 HashMap 判断标准相同, 两个元素的 hashCode 相等并且通过 equals() 方法返回 true;

15. HashTable 有了解过吗?

区别HashTableHashMap
数据结构数组 + 链表数组 + 链表 + 红黑树
是否可以为nullkey 和 value 都不能为 null可以为 null
hash算法key 的 hashCode()二次 hash
扩容方式当前容量翻倍 + 1当前容量翻倍
线程安全同步 (synchronized 加在方法上) 的,线程安全非线程安全

在实际开中不建议使用 HashTable,在多线程环境下可以使用 ConcurrentHashMap 类

  • 推荐文章

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

防火墙配置实验

配置 配置IPSec FW1 FW3 NAT策略 FW1 FW3 安全策略 FW1 FW3 最后测试

数仓实战——京东数据指标体系的构建与实践

目录 一、如何理解指标体系 1.1 指标和指标体系的基本含义 1.2 指标和和标签的区别 1.3 指标体系在数据链路中的位置和作用 1.4 流量指标体系 1.5 指标体系如何向上支撑业务应用 1.6 指标体系背后的数据加工逻辑 二、如何搭建和应用指标体系 2.1 指标体系建设方法—OS…

分布式定时任务调度xxl-job

1. xxl-job基本介绍 1.1 Quartz的体系结构 Quartz中最重要的三个对象:Job&#xff08;作业&#xff09;、Trigger&#xff08;触发器&#xff09;、Scheduler&#xff08;调度器&#xff09;。 xxl-job的调度原理:调度线程在一个while循环中不断地获取一定数量的即将触发的Tr…

AIGC启示录:深度解析AIGC技术的现代性与系统性的奇幻旅程

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

政安晨:【深度学习处理实践】(三)—— 处理时间序列的数据准备

在深度学习中&#xff0c;对时间序列的处理主要涉及到以下几个方面&#xff1a; 序列建模&#xff1a;深度学习可以用于对时间序列进行建模。常用的模型包括循环神经网络&#xff08;Recurrent Neural Networks, RNN&#xff09;和长短期记忆网络&#xff08;Long Short-Term M…

【框架设计】MVC、MVP、MVVM对比图

1. MVC&#xff08;Model-View-Controller&#xff09; 2. MVP&#xff08;Model-View-Presenter&#xff09; 3. MVVM&#xff08;Model-View-ViewModel&#xff09;

ChatGPT Plus 支付出现「您的银行卡被拒绝/your card has been declined」怎么办?

ChatGPT Plus 支付出现「您的银行卡被拒绝/your card has been declined」怎么办&#xff1f; 在订阅 ChatGPT Plus 或者 OpenAI API 时&#xff0c;有时候会出现已下报错 &#xff1a; Your card has been declined. 您的银行卡被拒绝 出现这种错误&#xff0c;有以下几个解…

创邻科技获评环紫金港创新生态圈智源创新企业

3月1日&#xff0c;由杭州城西科创大走廊管理委员会指导&#xff0c;中共杭州市西湖区委员会、西湖区人民政府主办的“环紫金港创新生态圈”行动推进大会暨2024年紫金港科技城经济高质量发展大会在杭州举办。凭借重要的生态位置和创新业务成果&#xff0c;创邻科技受邀参会并被…

瑞芯微 | I2S-音频基础分享

1. 音频常用术语 名称含义ADC&#xff08;Analog to Digit Conversion&#xff09;模拟信号转换为数字信号AEC&#xff08;Acoustic Echo Cancellor&#xff09;回声消除AGC&#xff08;Automatic Gain Control&#xff09;自动增益补偿&#xff0c;调整MIC收音量ALSA&#xf…

深入探索Transformer时代下的NLP革新

《基于GPT-3、ChatGPT、GPT-4等Transformer架构的自然语言处理》主要聚焦于如何使用Python编程语言以及深度学习框架如PyTorch和TensorFlow来构建、训练和调整用于自然语言处理任务的深度神经网络架构&#xff0c;特别是以Transformer为核心模型的架构。 书中详细介绍了Transf…

07.axios封装实例

一.简易axios封装-获取省份列表 1. 需求&#xff1a;基于 Promise 和 XHR 封装 myAxios 函数&#xff0c;获取省份列表展示到页面 2. 核心语法&#xff1a; function myAxios(config) {return new Promise((resolve, reject) > {// XHR 请求// 调用成功/失败的处理程序}) …

【嵌入式高级C语言】9:万能型链表懒人手册

文章目录 序言单向不循环链表拼图框架搭建 - Necessary功能拼图块1 创建链表头信息结构体 - Necessary2 链表头部插入 - Optional3 链表的遍历 - Optional4 链表的销毁 - Necessary5 链表头信息结构体销毁 - Necessary6 获取链表中节点的个数 - Optional7 链表尾部插入 - Optio…

git克隆过程报错

设置 git config 来强制 git 使用 HTTP 1.1 git config --global http.version HTTP/1.1想将其设置回 HTTP2&#xff0c;你可以这样做 git config --global http.version HTTP/2

飞驰云联CEO朱旭光荣获“科技领军人才”称号

2024年2月29日&#xff0c;苏州工业园区“优化营商环境暨作风效能建设大会”成功举办&#xff0c;会上公布了2023年度苏州工业园区第十七届第一批金鸡湖科技领军人才名单&#xff0c;Ftrans飞驰云联创始人兼CEO朱旭光先生凭借在数据安全以及文件交换领域取得的突出成果&#xf…

Feign实现微服务间远程调用续;基于Redis实现消息队列用于延迟任务的处理,Redis分布式锁的实现;(黑马头条Day05)

目录 延迟任务和定时任务 使用Redis设计延迟队列原理 点评项目中选用list和zset两种数据结构进行实现 如何缓解Redis内存的压力同时保证Redis中任务能够被正确消费不丢失 系统流程设计 使用Feign实现微服务间的任务消费以及文章自动审核 系统微服务功能介绍 提交文章-&g…

【k8s管理--两种方式安装prometheus】

1、k8s的监控方案 1.1 Heapster Heapster是容器集群监控和性能分忻工具&#xff0c;天然的支持Kubernetes和CoreOS。 Kubernetes有个出名的监控agent–cAdvisor。在每个kubernetes Node上都会运行cAdvisor&#xff0c;它会收集本机以及容器的监控数(cpu,memory,filesystem,ne…

【.NET Core】深入理解IO - FileSteam流

【.NET Core】深入理解IO - FileSteam流 文章目录 【.NET Core】深入理解IO - FileSteam流一、IO流概述二、文件流FileStream2.1 FileStream概述2.2 FileStream检测流位置更改2.3 FileStream构造函数2.4 FileStream常用属性2.5 FileStream.Read方法2.6 FileStream.Write方法2.7…

尚硅谷JavaScript高级学习笔记

01 准备 JavaScript中函数是对象。我们后续描述构造函数的内存模型时&#xff0c;会将构造函数称为构造函数对象。 02 数据类型 typeof 运算符来查看值的类型&#xff0c;它返回的是类型的字符串值 会做数据转换 03 相关问题 04数据_变量_内存 05相关问题1 06相关问题2 …

植物病虫害:YOLO玉米病虫害识别数据集

玉米病虫害识别数据集&#xff1a;玉米枯萎病&#xff0c;玉米灰斑病&#xff0c;玉米锈病叶&#xff0c;粘虫幼虫&#xff0c;玉米条斑病&#xff0c;黄二化螟&#xff0c;黄二化螟幼虫7类&#xff0c;yolo标注完整&#xff0c;3900多张图像&#xff0c;全部原始数据&#xff…

学习人工智能:吴恩达《AI for everyone》2019 第4周:歧视,攻击,发展中国家,就业

吴恩达 Andrew Ng&#xff0c; 斯坦福大学前教授&#xff0c;Google Brain项目发起人、领导者。 Coursera 的联合创始人和联合主席&#xff0c;在 Coursera 上有十万用户的《机器学习》课程&#xff1b;斯坦福大学计算机科学前教授。百度前副总裁、前首席科学家&#xff1b;谷…