Java集合基础知识点复习

这篇具有很好参考价值的文章主要介绍了Java集合基础知识点复习。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Java提供的常见集合

主要分为两类:

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

List

ArrayList底层实现与扩容机制

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

Java集合基础知识点复习,java,java,windows,python,开发语言


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
参考这篇博客。文章来源地址https://www.toymoban.com/news/detail-844696.html

到了这里,关于Java集合基础知识点复习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 集合基础知识点

    当 Java 程序中需要存放数据的时候,通常会定义变量来实现数据的存储,但是,当需要存储大量数据的时候该怎么办呢?这时首先想到的是数组,但是!数组只能存放同一类型的数据,而且其长度是固定的,那怎么办了?集合便应运而生了。 Java 集合类存放在 java.util 包中,

    2024年02月03日
    浏览(39)
  • java基础之线程知识点

    操作系统(OS)中并发(同时)执行的多个程序任务 宏观并行,微观串行 在一个时间段内,CPU会将时间段划分为若干个时间片,一个时间片是能被一个程序拥有,且只有拥有时间片的程序才能执行自身内容,所以当时间片的划分足够细小,交替频率足够快,就会形成并行的假象,时间上仍然是

    2024年02月01日
    浏览(32)
  • Pytorch基础知识点复习

    本篇博客是本人对pytorch使用的查漏补缺,参考资料来自 深入浅出PyTorch,本文主要以提问的方式对知识点进行回顾,小伙伴们不记得的知识点可以查一下前面的教程哦。   现在并行计算的策略是 不同的数据分布到不同的设备中,执行相同的任务(Data parallelism) 。   它的逻

    2024年01月20日
    浏览(31)
  • Java基础-知识点03(面试|学习)

    String 类是Java中用于表示字符串的类。在Java中,字符串是一系列字符的序列,用于表示文本数据。 String类的作用及特性 1、 创建字符串 : 可以通过字符串字面量或使用new来创建字符串对象。 2、 字符串长度 : 可以使用length()方法获取字符串的长度。 3、 字符串连接

    2024年04月15日
    浏览(40)
  • Java面试知识点(全)- Java面试基础部分一

    Java面试知识点(全)https://nanxiang.blog.csdn.net/article/details/130640392 语法基础 封装 利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联

    2024年02月05日
    浏览(29)
  • java基础知识点系列——分支语句(六)

    流程控制语句分类 顺序结构 分支结构 循环结构 顺序结构 顺序结构是程序中最简单最基本的流程控制,没有特定的语法结构,按照代码的先后顺序,依次执行。 if语句格式1 执行流程: 首先计算关系表达式的值 如果关系表达式的值为true就执行语句体 如果关系表达式的值为

    2024年02月02日
    浏览(33)
  • java相关知识点

    1.String和StringBuffer如何互相转化 StringBuffer buffer = new StringBuffer(string); String string = buffer.toString();  2.如何实现两个数组内容的拷贝  3.如何去除字符串首尾空格 str.trim()  4.字符串和字符数组如何相互转换 字符串转字符数组:str.toCharArray(); 字符数组转字符串:strs.valueOf(char[] ch)  

    2023年04月23日
    浏览(33)
  • 脚踏Java知识点

    三元运算符和if语句格式的区别 语法格式: 表达式执行: 返回值: 使用场景: switch语句 switch语句的基本语法如下: switch语句的执行流程如下: 需要注意的是: 下面是一个示例,演示了如何使用 switch 语句判断星期几: 循环结构 for循环: 具体执行过程为 while循环: 具体

    2024年02月13日
    浏览(30)
  • Java 面试知识点

    基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的 语法,集合的语法,io 的语法,虚拟机方面的语法。 和都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为 true 时,整个运算结果才为 true,否

    2024年02月16日
    浏览(29)
  • Java知识点:

    1、Java中的(8个)基本数据类型及其包装类:         byte(字节)                  8          -128 - 127                                                                     0         short(短整型)             16         -32768 - 32768               

    2024年02月13日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包