Java常见集合简述

Java常见集合

Java集合类主要由两个根接口CollectionMap派生出来的,Collection派生出了三个子接口: List、Set、Queue (Java5新增的队列),因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。
框架图如下:
在这里插入图片描述
在这里插入图片描述
上图中的绿色代表常用的实现类。

ArrayList

  • 底层使用Object数组实现,不保证线程安全。
  • 因为底层是数组,插入删除时间复杂度为O(n),可以快速随机访问,复杂度为O(1)。
  • 列表的结尾会预留一定的容量空间。

ArrayList与Vector区别

  • Vector是线程安全的,因为每个方法前都加了synchronized关键字。ArrayList线程不安全。
  • 扩容:ArrayList默认扩容至原来的1.5倍,Vector默认扩容至原来的2倍。

ArrayList与LinkedList区别

  • 两者都不保证线程安全
  • ArrayList底层是数组,LinkedList底层是双向循环链表

ArrayList扩容机制

  • 扩容本质是新建一个更大的数组,然后把原来数组的元素复制到新数组去。新建数组大小ArrayList默认是原来的1.5倍。
    在这里插入图片描述

HashMap

  • 底层是一个Node数组,数组每个元素可以看作一个key-value键值对。
  • 数组长度为2的幂次,主要为了能通过位运算获取key的索引位置,提升计算效率。
  • 产生哈希冲突时,使用拉链法,数组每个元素即为一个链表。要获取元素需要先经哈希计算得到数组下标,再从对应下标的链表上遍历找到相同key值的元素。遍历链表的时间复杂度为O(n),如果链表过长则效率不理想。所以当链表长度大于8并且整个数组容量大于64时,链表会转换成红黑树。但是如果数组长度小于64,会选择先进行数组扩容,而不是转换为红黑树。

HashMap的扩容机制

  • 默认扩容为原数组长度的2倍。在初始化时如果没有指定容量大小,则从0扩容到16。如果指定了初始大小,则Hash表大小为最接近指定容量且大于指定容量的2的幂次。
  • 扩容过程中需要重新哈希,重新哈希的过程巧用了2次幂的扩展之后,元素的位置要么在原位置,要么在原位置再移动2次幂的位置这个特点,直接将key的hash值和oldCap相与,如果为0,则保持原位,如果为1,则放⼊到原位+oldCap的位置。

HashMap的key

一般用Integer、String这种不可变类为key,String最为常用。

  • 字符串不可变,在创建的时候hashcode被缓存,不需要重新计算。
  • 获取对象时需要用到equals()和hashcode(),这两个方法需要正确被重写,在String类中已经很规范重写了这两个方法。

HashMap为什么线程不安全

  • 多线程下put操作会导致数值被覆盖
  • 多线程下一边扩容重新哈希一边get会导致get到null。

ConcurrentHashMap的实现原理

  • jdk1.7使用分段锁
  • jdk1.8抛弃分段锁,使用CAS+synchronized实现更低粒度的锁 ,只需要锁住链表头结点,就不会影响其它哈希桶元素的读写,大大提高了并发度。
  • ConcurrentHashMap的put逻辑(jdk1.8):根据key计算hash值,判断是否需要初始化,定位到node,拿到首节点f,如果首节点为null,则通过CAS尝试添加元素,如果f.hash = MOVED = -1 ,说明其它线程在扩容,参与一起扩容。如果都不满足,就用sychronized锁住f节点,判断是链表还是红黑树,遍历插入。
  • ConcurrentHashMap的get逻辑(jdk1.8):get方法不需要加锁,因为node的val和指针next都用了volatile修饰,修改val或者新增节点对其它线程都可见。
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值