java8中容器类扩容一览(编辑中)

本文详细梳理了Java8中各种容器类的扩容策略,包括List(ArrayList、Vector、CopyOnWriteArrayList)、Set(HashSet、LinkedHashSet)、Map(HashMap、LinkedHashMap、ConcurrentHashMap)、Stack、Queue(ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、DelayedWorkQueue)以及Dictionary(Hashtable)。文章介绍了每个容器的默认大小、扩容因子、扩容方式,并对一些特殊容器如ThreadLocal的扩容进行了讨论。
摘要由CSDN通过智能技术生成

 

 

 

统计过程中,忽略特殊处理过程,譬如:当大于Integer.MaxValue()-8时、当小于默认值时等等等。每个容器特殊处理不一样。这里不做多余赘述,是为了帮助博主以及读者们整理一般情况下,常见容器的扩容策略的记忆而已

 

 

 

 

文章中几个相关名词可以做个科普:

  • 扩容因子:0~1之间,当实际填充占总容量多少时,触发扩容动作
  • 扩容方式:譬如:2倍。则按照新的容量newSize为oldSize*2

 

 

 

目录

1、Collection

      1.1、List 

                      1.1.1、ArrayList

                      1.1.2、Vector

                      1.1.3、CopyOnWriteArrayList

     1.2、Set

                      1.2.1、HashSet

                      1.2.2、LinkedHashSet

     1.3、map

                      1.3.1、HashMap

                      1.3.2、LinkedHashMap

                      1.3.3、ConcurrentHashMap

     1.4、Stack

     1.5、Queue

                      1.5.1、ArrayBlockingQueue

                      1.5.2、LinkedBlockingQueue

                      1.5.3、PriorityBlockingQueue

                      1.5.4、SynchronousQueue

                              1.5.4.1、TransferStack

                              1.5.4.2、TransferQueue

                              1.5.4.3、TransferQueue和TransferStack的一些源码比较和思考

                      1.5.5、DelayedWorkQueue

2、Dictionary

      2.1、Hashtable

3、特殊

      3.1、ThreadLocal


1、Collection

      1.1、List 

                      1.1.1、ArrayList

默认大小10,增加1.5倍,扩容因子:申请不到新的空间

              

这里需要稍加留意,作为最基础的容器,在java11后,有本质区别,具体是哪个版本开始修改的,作者也没有去深究,这是11的源码,扩容因子:1,扩容方式+1

 

 

 

 

 

                      1.1.2、Vector

这里有一个capactityIncrement的值,是通过构造器Vector(int,int)中第二个param传入,若是不指定,默认为0。默认情况下,当满了就2倍扩容

 

 

 

 

 

                      1.1.3、CopyOnWriteArrayList

这也是一个蛮重要的并发容器,但他的扩容方式仿佛并不是那么高效,虽然它足够的安全,不过也足以使得我们认清他的定位了。

 

初始0

 

扩容因子是满了就扩,而扩容方式,+1

 

 

 

 

 

 

 

     1.2、Set

                      1.2.1、HashSet

众所周知,HashSet的实现方式其实就是以HashMap作为状态变量以实现的。所以,这里不做赘述,参照:HashMap

                      1.2.2、LinkedHashSet

众所周知,HashSet的实现方式其实就是以LinkedHashMap作为状态变量以实现的。所以,这里不做赘述,参照:HashMap

 

 

 

 

 

 

 

     1.3、map

                      1.3.1、HashMap

这里有很多复杂的处理,当然我们只查看hash表部分扩容策略,并且也只在意常规状态下的扩容。这里默认扩容因子为0.75,扩容方式为2倍扩容,默认大小为16。注意!导致HashMap触发扩容有两种情况,1、当某个节点 > 7时,并且tab长度小于64时,这是不会转换红黑树,而是扩容。2、当tab的填充率达到扩容因子时

(同时,红黑树转换链表的untreeify方法,也是在resize中触发,当桶中树节点小于等于6时拆解)

              

 

 

 

 

 

 

                      1.3.2、LinkedHashMap

这里LinkedHashMap也不多做赘述,他的本质是继承了HashMap在那基础之上所作的修改,节点继承,增加新的指针,newNode修改,生成新的拓展节点类

 

 

                      1.3.3、ConcurrentHashMap

关于ConcurrentHashMap,博主想了半天,这部分蛮重的,关于源码部分,他的实现很复杂,博主研究了好几天,都未看懂,这里有必要列出来详细叙述:

 

 

看源码有几种方式,个人认为比较实用的有三种:一个是边看源码根据经验,大块大块的划分代码块的作用途径,通过看到关键字和算法的方式,比较囫囵吞枣,一般经验丰富的程序猿快速看源码都是这样。第二种时,我边运行极限状态,边进行debug,这种方式优点很明显,每句什么用,一清二楚。第三种是“正面刚”,一行一行死扣,罗列各种情况去一一排除。

这里,我觉得有必要用第三种,毕竟这是一个大家耳熟能详的很重要的一个并发容器类,我们需要假设最坏情况,很多个线程大量并发的执行到每一步,会发生什么。相信,这也是我们在构筑安全的并发方法时也经常做的假设。

 

 

 

首先是数组初始化的时候,他不同于大多容器,是在put时候判断为空初始化的。

初始化:

关于这里,如下图中:

SizeCTL是个蛮重点的状态变量,可以看到,通过原子的CAS初始为-1,初始容量为DEFAULT_CAPATITY,即是16,截至到这里为止,全程并未同步,并且若是有线程并发的去执行到(sc > 0) ? sc : DEFAULT_CAPATITY这一句时也是有可能的。要么sc=SIZECTl=0,要么sc=SIZECTL=n - (n >>> 2),若是第二个线程来到这里,sc为12,n为12;若是第三个线程到了这里,sc为9,n为9;第四个线程,sc为7,n为7;第五个,sc为6,n为6;第六个,5,5........最后恒定在sc为3,n为3。

看到这里,心里面不禁会问,是不是哪里错了?没错,是错了。以上是比较单细胞的单线程思想去做的理解,也可以说是我们忽略了unsafe的全部作用

U.compareAndSwapInt(this, SIZECTL, sc, -1)

这里不仅保证了SIZECTL必须等于sc时才能进入,也使得SIZECTL内存区域赋值为了-1,简单来说,利用了CAS实现了一个粗略的悲观锁,当有其他参与初始化的线程来到这里时,发现有线程正在进行初始化,那么,便会主动让出cpu执行权,Thread.yield()保证了这一点,若是第二个线程进来时,前一个线程已经运行完毕finally部分,那么也由try中的if语句驱赶出了创建初始化区域。保证了线程安全。

因此,初始化的结论是:默认大小16,并且多线程并发初始化的话,利用CAS加简单的悲观锁,其他线程发现有线程正在初始化的话,便会让出资源,等到某个线程初始化完成。

 

 

 

关于插入没什么好说的,似乎与我们关注的扩容特征无关,他与hashMap思想类似,链表转为红黑树

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值