Java集合——List与Map详解

集合

一、迭代器、forEach、for

二、ArrayList和LinkedList底层原理

1.List集合是线程不安全的

经常出现java.util.ConcurrentModificationException(并发修改异常)
1.使用Vector类 效率低
2.使用Collections集合工具类,对集合进行同步处理:
List<String> list = Collections.synchronizedList(new ArrayList<>()); 
但是在多线程开发中,对其进行遍历,需要添加 synchronized 关键字,因为List的 add、index 等方法中都是带有synchronized 关键字,
但是在 iterator 中没有synchronized 关键字。
3.使用CopyOnWriteArrayList(写时复制,读写分离的思想) CopyOnWrite容器即写时复制的容器,往一个容器添加元素的时候,不直接往当前容器添加,
而是先将当前容器进行复制,然后往新的容器里添加元素,添加完元素之后,再将原容器引用指向新容器;这样做的好处是对容器进行并发的读,而不需要加锁,
因为当前容器不会添加任何元素,在add一些方法内部设置有Reentrantlock锁。

2.CopyOnWriteArrayList

CopyOnWriteArrayList如何保证的线程安全

请添加图片描述

三、Set集合及Tree结构

四、HashMap底层原理

1.优化

1.8之后hashmap对hash算法和寻址算法的优化
区别:jdk1.8中hash算法是让key的hash值的高16位和低16位进行异或运算(如果a、b两个值不相同,则异或结果为1。
如果a、b两个值相同,异或结果为0),寻址算法是取代之前的对hash进行取模运算,而是用hash&(数组长度-1),效果和
取模是一样的,但是要求数组的长度必须是2的n次方,这也是为什么hashmap一定是2倍扩容。
(1)改进优点在于,让高低16位都参与到寻址计算,可以减少之前低16位决定寻址结果的概率,因为数组长度一般都比较小,
数组长度-1的高16位大概率都是0,那么与出来的结果高16位也都是0,这样计算结果大多数都是靠着低16,寻址有较高的hash碰撞;
(2)与运算的性能要远高于取模运算。
查询链表复杂度O(n),红黑树查询复杂度O(logn)

hashmap扩容机制
当16位扩容到32位时候
对原来的key的hash值进行rehash,因为在二进制上,3216只多出来一个1,rehash的结果如果比原来的值多1,那么直接在
原来的位置加上一个数组扩容的长度,如果位数没有变,则这个key的位置也不变。

HsahMap扩容机制

请添加图片描述

2.Hash冲突及解决方法

  • 重哈希法,开放地址法,建立公共溢出,链地址法。

3.HashMap双链死循环

双链循环是JDK1.7及更早的版本之前才有的问题。在多线程扩容的情况下,一个线程执行到一半,还未扩容,而另一个线程却抢走先行扩容了,
这时候可能出现第一个线程的元素与第二个线程中的元素相互引用的情况,相互引用就会造成死锁。

比如一个数线长度为4,有两个数,一个为2,一个为10,那么这两个数都会在索引2上形成哈希桶结构,此时进行扩容,本来在新数组中是2指向10的,
结果但之前那个前程正好断在10指向新数组的中间,这就会导至10又重新指向2,最终导while判断中的e永远不会等于null,造成死循环。

JDK1.8版本避免了双链循环,但不是完全避免,看过一些测试文章,红黑树之间也可能出现死循环,只是比较1.7版本,几率降低

4.HashMap为什么数组长度始终是2的n次方

如果不是2的次幂的数的话 假设数组长度是一个奇数,那参与最后的&运算的肯定就是偶数,那偶数的话, 它二进制的最后一个低位肯定是00做完&运算得到的肯定也是0,那意味着&完后得到的数的最低位一定是0 最低位一定是0的话,那说明一定是一个偶数,换句话说就是:
&完得到的数一定是一个偶数,所以&完获取到的脚标永远是偶数位,那意味着奇数位的脚标永远都没值,有一半的空间是浪费的 奇数说完了,
来说一下偶数,假设数组长度是一个偶数,比如6,那参与&运算的就是5 5的二进制 00000000 00000000 00000000 00000101 发现
任何一个数&5,倒数第二低位永远是0, 那就意味着&完以后,最起码肯定得不出2或者3(这点刚开始不好理解,但是好好想一下就能明白)
 意味着第二和第三脚标位肯定不会有值 所以不是2的次幂的话,不管是奇数还是偶数,就肯定注定了某些脚标位没值,注定了某些脚标位永远是没有值的。

5.put的流程

首先对hash值的高16位和低16位进行异或运算,然后对数组长度-1进行与运算,计算出插入数组的位置,如果数组中没有元素,则直接插入,
如果存在则比较key值,如果相同则覆盖,如果不同则插入尾部,如果链表达到8则转为红黑树,最后判断是否达到扩容指标,如果达到则扩容。

6.Hashtable与HashMap有什么区别

7.ConcurrentHashMap,JDK7版本跟JDK8版本有什么不同?

ConcurrentHashMap1.7版本:
创建对象
1、默认创建一个长度16,加载因子为0.75的大数组,但这个大数组一但创建无法扩容,所以加载因子是给小数组用的。
2、还会创建一个长度为2的小数组,把地址值赋值给0索引处。其他索引位置的元素均为null。
 插入元素
第一次插入新元素时,会根据键的哈希值来计算出在大数组中应存入的位置。
•	如果为null,则按照模板创建小数组,大数组只用来存放地址值。
    o	创建完毕,会进行二次哈希,计算出在小数组中应存入的位置。
	o	直接存入。
•	如果不为null,就会根据记录的地址值找到小数组。
    o	二次哈希,计算出在小数组中应存入的位置。
    o	如果需要扩容,则先将小数组扩容2倍。
    o	如果不需要扩容,则判断小数组的这个位置有没有元素。
	如果没有元素,则直接存。
	如果有元素,就会调用equals方法,比较属性值
•	如果equals为true,相同则不存;
•	如果equals为false,新元素替换老元素,老元素挂在新元素下面,形成哈希桶结构(链表)。
/*线程安全
1.7采用Segment+HashEntry分段锁的机制,也就是一个Segmen的数组,Segment继承Reentarntlock,
Segment下面包含小数组HashEntry,HashEntry本身是一个链表的结构,实际上每一个segment都是一个
hashmap,默认长度为16,最多同时访问16个线程。*/

ConcurrentHashMap1.8版本:
区别1.7:
•	底层结构改变:
哈希表(数组会扩容)——【数组】+【链表】+【红黑树】
1.7是旧元素挂新元素下面(旧挂新——头插法);1.8是新元素挂在旧元素下面(新挂旧——尾插法)!
/*•	线程安全机制改变:结合CAS机制+synchronized同步代码块形式保证线程安全。进行put时候,当一个
数组的某一个位置是null,那么多个线程读到这个null,会执行CAS,同一时间,只有一个线程能执行成功;这时
别的线程就会基于链表或者红黑树+synchronized,是头节点进行加锁,把数据插入进去*/

    
如果该索引为null,则利用cas算法,将本结点添加到数组中。(第一次添加用CAS算法)
如果该索引不为null,则利用volatile关键字获得当前位置最新的结点地址,新元素挂在旧元素下面。(因为1.8后有了红黑树,
会自动进行排序调整,再调整链表头就浪费资源了,而旧版本需要后进先出)
(红黑树转化条件见HashMap底层)

有元素后,再对该元素进行操作时,会给头结点(第一个元素)做为锁对象,加上synchronized同步代码块(锁对象的方式),
保证线程安全。

•	加载机制改变:懒加载——第一次添加元素时初始化数组。(添加元素时,判断数组是否为空,或者长度为0,如果是,
就初始化数组)

/*CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。
整个比较并替换的操作是一个原子操作。
CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它
线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。   
概述
CAS(Compare-and-Swap),即比较并替换,是一种实现并发算法时常用到的技术,Java并发包中的很多类都使
用了CAS技术
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,
否则什么都不做。通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新值 B,然后使用
 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功*/


版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340