Java基础(集合篇)

前言

Java基础中,集合是面试时经常会被问到的一部分,所以这篇文章将简单做一个总结,首先会讲述集合中Collection接口中的List和Set,然后重点会讲述Map接口,包括HashMap为什么多线程扩容的时候回造成死循环。

Java集合框架

Java集合

List

List中比较常用的三个是ArrayList、LinkedList以及Vector。

  • ArrayList:最底层由数组实现,初始容量为10。
  • LinkedList:最底层由一个双向循环链表实现。
  • Vector:最底层由数组,和上面的区别是Vector是一个线程安全的容器,大部分操作包含关键字synchronized,但是相对的效率没有上面两者高。

ArrayList和Vector都是一个动态数组,当空间不够的时候回发生扩容,ArrayList会扩容至原来的1.5倍,而Vector默认情况下会扩容至原来的2倍。但是如果扩容之后还是无法装下元素,则需要扩容至最小容量。
Vector可以在创建的时候指定其容量增量capacityIncrement但是ArrayList不可以),在指定其容量增量之后,发生扩容的时候,先判断其容量增量是否大于0,如果大于0,则增加capacityIncrement的大小,否则增加为之前的2倍。

Set

常用的Set主要有两个:

  • HashSet:使用hashcode和equal方法来判断进入集合的元素是否相同,底层实现是HashMap,线程不安全。
  • TreeSet:可以排序的Set,底层实现是二叉树,线程不安全。

Map

Map集合和上面两种集合不同,有一个属于自己的Map接口,常用的HashMap都从这个接口实现。Map集合相比上面的两个集合复杂不少,也是面试时候喜欢问的地方,常用的Map有以下几类:

  • HashMap:线程不安全,Map中的Key和Value可以为null。
  • HashTable:线程安全,使用Synchronized关键字进行了加锁,Key和Value不能为空。
  • ConcurrentHashMap:线程安全,在JDK1.7和1.8有比较大的差别。
  • TreeMap:可以给Key排序的Map,采用红黑树实现。

HashMap的扩容问题

HashMap在多线程的情况下会出现死循环以及元素丢失的问题。主要是在Map扩容的时候,两个线程重复指向元素的原因(基于JDK1.7,JDK1.8之后采用了尾插法不再倒序插入,所以不会死循环,但是仍然是线程不安全的)。具体可以参考这里HashMap在并发情况下为什么造成死循环?
HashMap在默认情况下,初始的数组大小为16,扩容因子为0.75,也就是说当插入一个元素之后,如果数组已经达到了12个,那么需要进行扩容(1.7则是先扩容再插入),扩容后的大小为原来的2倍。HashTable的初始大小为11,每次扩容为原来的2倍+1。
所以HashMap的大小始终都是 2 n 2^n 2n,每次扩容之后,HashMap都需要再Hash,然后把原来的元素迁移到新的数组中。
HashMap的具体Hash方式是先对Key进行两次Hash,然后和数组长度进行取模操作。由于大小始终都是 2 n 2^n 2n,所以取模的时候相当于和 2 n − 1 2^n-1 2n1进行与操作,速度会快上不少。
在Java1.8之后,如果Map中的链表过长的话,则会转换为红黑树来加快查找速度。

ConCurrentHashMap

JDK1.7

在1.7中,ConCurrentHashMap采用Segment将数组划分为多个不同的区域,每次需要进行put的时候会先对整个Segment进行加锁操作,相比HashTable的给整个map加锁会快上不少。
KeyEntry中的元素例如Key、Value都采用volatile关键字修饰,确保修改之后各个线程之间可见。

JDK1.8

在1.8中,Map中的数据结构从KeyEntry变为了Node(HashMap也是如此),不过同样包含Key、Value等字段,区别并不是很大。
除此之外,比较重要的一点是抛弃了之前的Segment结构,采用了CAS和synchronized来保证线程安全性。

后记

其实在JDK1.7中和JDK1.8中,Map集合进行了比较多个改动,除了增加红黑树之外,还修改了Map中存放的数据结构等等,这一点在很多资料上并没有仔细的进行划分,甚至有些资料混为一谈,关于这一点这篇文章写得很好:
HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你!
下一篇文章将主要讲述Java中的基础类型和包装类的一些细节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值