Java集合基础知识点复习

目录

  • Java提供的常见集合
  • List
    • ArrayList底层实现与扩容机制
    • ArrayList list=new ArrayList(10)中的list扩容几次
    • 如何实现数组和List之间的转换
      • 用Arrays.asList转List后,如果修改了数组内容,list受影响吗?List用toArray转数组后,如果修改了List内容,数组受影响吗
    • ArrayList 和 LinkedList 的区别
    • 如何解决ArrayList 和 LinkedList 不是线程安全问题
  • HashMap
    • HashMap的实现原理
    • HashMap的扩容机制
    • hashMap的寻址算法
    • 为何HashMap的数组长度一定是2的次幂?
    • hashmap在1.7的多线程死循环问题
    • HashSet与HashMap的区别
    • HashTable与HashMap的区别
    • hashmap线程安全问题

Java提供的常见集合

主要分为两类:

  • 第一个是Collection 属于单列集合,第二个是Map 属于双列集合在Collection中有两个子接口List和Set。在我们平常开发的过程中用的比较多像list接口中的实现类ArrarList和LinkedList。 在Set接口中有实现类HashSet和TreeSet。
  • 在map接口中有很多的实现类,平时比较常见的是HashMap、TreeMap,还有一个线程安全的map:ConcurrentHashMap

List

ArrayList底层实现与扩容机制

ArrayList底层是用动态的数组实现的,初始容量为0,当第一次添加数据的时候才会初始化容量为10,在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组。

在这里插入图片描述


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

在ArrayList的源码中提供了一个带参数的构造方法,这个参数就是指定的集合初始长度,所以给了一个10的参数,就是指定了集合的初始长度是10,这里面并没有扩容。

第二个构造方法是无参构造函数,默认创建一个空集合


如何实现数组和List之间的转换

数组转list,可以使用jdk自动的一个工具类Arrars,里面有一个asList方法可以转换为数组
List 转数组,可以直接调用list中的toArray方法,需要给一个参数,指定数组的类型,需要指定数组的长度。


用Arrays.asList转List后,如果修改了数组内容,list受影响吗?List用toArray转数组后,如果修改了List内容,数组受影响吗

Arrays.asList转换list之后,如果修改了数组的内容,list会受影响!,因为它的底层使用的Arrays类中的一个内部类ArrayList来构造的集合,在这个集合的构造器中,把我们传入的这个集合进行了包装而已,最终指向的都是同一个内存地址
list用了toArray转数组后,如果修改了list内容,数组不会影响,当调用了toArray以后,在底层是它是进行了数组的拷贝,跟原来的元素就没啥关系了,所以即使list修改了以后,数组也不受影响


ArrayList 和 LinkedList 的区别

主要是底层使用的数据结构不一样,ArrayList 是动态数组,LinkedList 是双向链表,这也导致了它们很多不同的特点。

从操作数据效率来说

ArrayList按照下标查询的时间复杂度O(1)【内存是连续的,根据寻址公式】, LinkedList不支持下标查询查找(未知索引): ArrayList需要遍历,链表也需要链表,时间复杂度都是O(n)
ArrayList尾部插入和删除,时间复杂度是O(1);其他部分增删需要挪动数组,时间复杂度是O(n)
LinkedList头尾节点增删时间复杂度是O(1),其他都需要遍历链表,时间复杂度是O(n)

从内存空间占用来说

ArrayList底层是数组,内存连续,节省内存
LinkedList 是双向链表需要存储数据,和两个指针,更占用内存

从线程安全来说,ArrayList和LinkedList都不是线程安全的



如何解决ArrayList 和 LinkedList 不是线程安全问题

主要有两种解决方案:

  • 我们使用这个集合,优先在方法内使用,定义为局部变量,这样的话,就不会出现线程安全问题。

  • 如果非要在成员变量中使用的话,可以使用线程安全的集合来替代,但是使用同步锁属于下下策,会影响服务器性能,因为它会使并行变成串行。

    • ArrayList可以通过Collections 的 synchronizedList 方法将 ArrayList 转换成线程安全的容器后再使用。
    • LinkedList 换成ConcurrentLinkedQueue来使用

还可以使用ThreadLocal,把该属性绑定在本地线程中。



HashMap

HashMap的实现原理

JDK1.8之前采用的拉链法,数组+链表
JDK1.8之后采用数组+链表+红黑树,链表长度大于8且数组长度大于64则会从链表转化为红黑树


HashMap的扩容机制

  • 在添加元素或初始化的时候需要调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次扩容都是达到了扩容阈值(数组长度 * 0.75
  • 每次扩容的时候,都是扩容之前容量的2倍;扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新的数组中
  • 没有hash冲突的节点,则直接使用 e.hash & (newCap - 1) 计算新数组的索引位置
  • 如果是红黑树,走红黑树的添加;如果是链表,则需要遍历链表,可能需要拆分链表,判断(e.hash & oldCap)是否为0,如果是0,该元素的位置则停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上

hashMap的寻址算法

这个哈希方法首先计算出key的hashCode值,然后通过这个hash值右移16位后的二进制进行按位异或运算得到最后的hash值。
在putValue的方法中,计算数组下标的时候使用hash值与数组长度取模得到存储数据下标的位置,hashmap为了性能更好,并没有直接采用取模的方式,而是使用了数组长度-1 得到一个值,用这个值按位与运算hash值,最终得到数组的位置:e.hash & (newCap - 1)


为何HashMap的数组长度一定是2的次幂?

第一:
计算索引时效率更高:如果是 2 的 n 次幂可以使用位与运算代替取模
第二:
扩容时重新计算索引效率更高:在进行扩容时会进行判断 hash值按位与运算
旧数组长租是否 == 0 如果等于0,则把元素留在原来位置 ,否则新位置是等于旧位置的下标+旧数组长度


hashmap在1.7的多线程死循环问题

jdk7的的数据结构是:数组+链表 在数组进行扩容的时候,因为链表是头插法,在进行数据迁移的过程中,有可能导致死循环

比如说,现在有两个线程
线程一:读取到当前的hashmap数据,数据中一个链表,在准备扩容时,线程二介入

线程二也读取hashmap,直接进行扩容。因为是头插法,链表的顺序会颠倒过来。比如原来的顺序是AB,扩容后的顺序是BA,线程二执行结束。

当线程一再继续执行的时候就会出现死循环的问题。
线程一先将A移入新的链表,再将B插入到链头,由于另外一个线程的原因,B的next指向了A,所以B->A->B,形成循环。

当然,JDK 8 将扩容算法做了调整,不再将元素加入链表头(而是保持与扩容前一样的顺序),尾插法,就避免了jdk7中死循环的问题。


HashSet与HashMap的区别

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


HashTable与HashMap的区别

第一,数据结构不一样,hashtable是数组+链表,hashmap在1.8之后改为了数组+链表+红黑树
第二,hashtable存储数据的时候都不能为null,而hashmap是可以的
第三,hash算法不同,hashtable是用本地修饰的hashcode值,这可能导致更多的哈希冲突,从而影响性能。而hashmap经常了二次hash(可以看上面的寻址算法)
第四,扩容方式不同,hashtable是当前容量翻倍+1,hashmap是当前容量翻

第五,hashtable是线程安全的,操作数据的时候加了锁synchronized,hashmap不是线程安全的,效率更高一些


hashmap线程安全问题

hashmap不是线程安全的,推荐采用ConcurrentHashMap进行使用,它是一个线程安全的HashMap
参考这篇博客。

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

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

相关文章

uniapp项目问题及解决(前后端互联)

1.路由跳转的问题 uni.navigateTo() 保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面 uni.redirectTo() 关闭当前页面,跳转到应用内的某个页面。 uni.reLaunch&…

揭开Spring Bean生命周期的神秘面纱

目录 一、Spring IOC 1.1 Spring IOC 的加载过程 二、Spring Bean 生命周期 2.1 实例化前置 2.2 实例化后置 2.3 属性赋值 2.4 初始化前置 2.5 初始化 2.6 初始化后置 2.7 Bean 销毁 Spring 是一个开源的企业级Java应用程序框架,它简化了企业级应用程序开…

开源数据湖iceberg, hudi ,delta lake, paimon对比分析

Iceberg, Hudi, Delta Lake和Paimon都是用于大数据湖(Data Lake)或数据仓库(Data Warehouse)中数据管理和处理的工具或框架,但它们在设计、功能和适用场景上有所不同。 Iceberg: Iceberg是用于大型分析表的高性能格式。Iceberg将SQL表的可靠性和简易性带入到大数据领域,同…

【java的本地锁到分布式锁介绍】

文章目录 1.java本地自带锁介绍及应用synchronized(1)synchronized原理和优化(2)synchronized作用(3)synchronized的使用 CAS(1) CAS原理(2)CAS和synchronized优缺点 lock 2.分布式锁…

idea常用配置

IDEA设置全局配置 参考:IDEA设置全局配置_idea如何打开一个项目,全局设置-CSDN博客 idea提交代码到git或svn上时,怎么忽略.class、.iml文件和文件夹等不必要的文件 参考:idea提交代码到git或svn上时,怎么忽略.class、.iml文件和文…

LeetCode-74. 搜索二维矩阵【数组 二分查找 矩阵】

LeetCode-74. 搜索二维矩阵【数组 二分查找 矩阵】 题目描述:解题思路一:先二分查找行,再二分查找列。解题思路二:暴力遍历,也能过。解题思路三:用python的in。 题目描述: 给你一个满足下述两条…

VSCODE目录树缩进调整

VSCode默认的缩进太小了,简直看不出来,很容易弄混目录。在设置里修改就行了。 修改后效果:

何为网络协议?一图知晓网络过程。

网络协议就是计算机之间沟通的语言 为了有效地交流,计算机之间需要一种共同的规则或协议, 就像我们和老外沟通之前,要先商量好用哪种语言, 要么大家都说中文,要么大家都说英语,这才能有效地沟通。 网络协…

OSPF中配置静态路由负载分担实验简述

OSPF中配置静态路由负载分担 实验简述 在静态路由负载分担中,多个路由器被配置为共享负载的目标,以实现流量的均衡分配。 到达目的地有N条相同度量值的路径,默认值60,N条路由是等价路由,数据报文在N条链路上轮流发送。…

探索基于WebRTC的有感录屏技术开发流程

title: 探索基于WebRTC的有感录屏技术开发流程 date: 2024/4/7 18:21:56 updated: 2024/4/7 18:21:56 tags: WebRTC录屏技术屏幕捕获有感录屏MediaStream实时传输音频录制 第一章:技术原理 WebRTC(Web Real-Time Communication)是一种开放源…

SQL Sever 2008 安装教程

先从官网下载程序:下载地址 打开上述链接后,点击下载按钮。 就会跳出下面这个界面,如果你的电脑是64位的请选择下图中这两个程序。 下载完成后,在电脑磁盘中找到这两个文件,注意安装的顺序,先安装 SQLEXPR…

Linux:软硬链接及动静态库

一、Linux中的链接文件 1.1硬链接及应用场景 ln//创建硬链接 我们再创建一个硬链接生成的文件。 我们可以看到mlink.hard的inode和makefile.c的inode都是一样的,inode一样里面的数据自然也是一样。相当于对make.file进行了一个重命名,所以硬链接一定没…

2023年蓝桥杯省赛——买二赠一

目录 题目链接:1.买二赠一 - 蓝桥云课 (lanqiao.cn) 题目描述 输入格式 输出格式 样例输入 样例输出 样例说明 思路 队列贪心 代码实现 总结 题目链接:1.买二赠一 - 蓝桥云课 (lanqiao.cn) 题目描述 某商场有 N 件商品,其中第 i 件…

漫谈:“标准”是一种幻觉 C++语言标准的意义

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 “标准”这个词很迷惑&#xf…

铸铁平台的单围和双围是什么——北重机械

铸铁平台的单围和双围是指平台的围栏结构。单围指平台只有一面围栏,通常用于平台的三个边界上,另一边是与建筑物相连的。双围指平台两侧围栏都有,即平台四个边界都有围栏。双围结构比单围结构更加安全,可以防止人员和物品从平台四…

CleanmyMac 苹果电脑清理软件,为你的 Mac 提速!

Apple Macbook 已成为当今职场不可或缺的高效助手,尤其在普遍的远程办公场景下,其运行流畅度对工作效率及用户体验至关重要。虽然长期使用会使Mac电脑性能自然衰退,但大部分导致系统变慢的因素其实可经由用户自行调整得到显著改善&#xff0c…

linux 设置命令输入行高亮(与软件无关:xshell等)

在命令执行后输出内容比较多的情况下,很难查看自己的历史命令 这个配置是系统的配置:取消.bashrc文件中force_color_prompt=yes的注释即可 (和连接服务器的软件无关) 具体的操作如下: 执行以下命令,查看配置所在的行数root@hecs-166280:~# cat .bashrc -n | grep force_…

Java Lambda 表达式(详细)

Java Lambda 表达式 Lambda 的发展史 Java Lambda 表达式是在 Java 8 版本中引入的重要特性,它描述了一种更简洁、更灵活的方式来处理函数式编程。 在 Java 8 之前,要实现函数式编程,需要通过匿名类实现接口的方式。这样的代码通常比较冗长…

深入MyBatis的动态SQL:概念、特性与实例解析

MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。它可以使用简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO,即普通的 Java 对象为数据库中的记…

MacOS Docker 部署 Redis 数据库

一、简介 Redis是一个开源的、使用C语言编写的、基于内存亦可持久化的Key-Value数据库,它提供了多种语言的API,并支持网络交互。Redis的数据存储在内存中,因此其读写速度非常快,每秒可以处理超过10万次读写操作,是已知…