集合分类

  • List:有序的,可重复

    • ArrayList:底层数据结构是数组,查询快,增删慢;原因是查询可直接定位到每一元素,而增删可能需要遍历所有元素;扩容相对消耗资源,原因是,需要创建一个新的数组,并将原有的元素复制到新数组中,故使用ArrayList时最好先确定其容量大小。
    • LinkedList:1.7之前底层数据结构是循环双向链表,查询慢,增删快;原因是链表定位某一元素可能需要遍历所有元素,而增删只需要改变元素的前置和后置应用即可,且无需扩容;1.7之后底层数据结构变成了双休链表,加了first和last节点来标志首尾,原因是更加清晰,且在最后插入元素也无需修改头结点,减少操作
    • Vertor:底层数据结构是数组,查询快,增删慢;原因是底层数据结构是数组;线程安全,效率低,原因是在所有操作数据的方法上加入了synchronized字段;
  • Set:无序的,唯一的

    • HashSet:底层数据结构是HashTable,实际实现是通过HashMap进行实现,值存储在HashMap的Key之中,而HashMap的Value值则是一个常量
    • TreeSet:底层数据结构是红黑树,通过红黑标记使二叉树保持平衡,降低树的高度,提高遍历效率,可通过元素实现Comparable和结合构造器中加入Comparator的方式进行排序

HashMap数据结构

HashMap在JDK1.7的数据结构是Hash表+链表,而在JDK1.8中做了优化,当链表长度达到8时,将链表转换为红黑树进行存储。如下图:
Java容器(Java复习一)
插入值的操作:首先计算key值的hash值,得到的计算结果为数组下标在HashTable中查找,查看对应位置是否有值,若无值则进行插入,若有值,查看key值是否相等,相等则覆盖,不相等则查看是否为链表,且链表的长度是否小于等于8,未达到则在链表后插入,若达到了则将链表转换为红黑树进行插入。具体查看下图:
Java容器(Java复习一)

HashMap为何线程不安全

  • jdk1.7中resize方法会造成链表循环,从而get时,会存在死循环;put方法时,两个数据都获取了桶的位置,插入时,导致一个元素被覆盖,从而数据丢失
  • jdk1.8中没有resize方法的问题,但是有put方法导致的问题

如何使Collection线程安全

  1. 通过线程安全的集合,如Vector和HashTable,效率较低,原因是在每个操作数据的方法上加入了synchronized字段,加锁的范围过大导致效率低下
  2. 通过CollectionUtils.synchronizedCollection方法来装饰集合,在操作数据的方法上加入了synchronized字段,加锁的范围过大导致效率低下
  3. 使用JUC包下的线程安全类,缩小加锁的范围,且通过自旋的方式来进行加锁,通过CPU计算来换取阻塞和唤醒的时间

ConcurrentHashMap

  • jdk1.7:采用的是分段锁的形式,将数组分割成多个小的数组来进行加锁,从而不同的段之间有不同的锁,减少了插入时竞争一把锁的情况,提高了效率。在进行Hash值计算时,需要定位两次,首先定位Segment数组,再定位到具体的位置。其中put数据需要加锁,get数据不需要加锁,size方法前三次先不加锁,自旋比较值是否一致,一致就返回,不一致则在每个Segment上加锁计算
  • jdk1.8:采用的是在每个数组节点的头部进行加锁。put方法首先判断有无Hash冲突,没有Hash冲突的情况下通过CAS插入,存在Hash冲突则通过加锁来保证线程安全,相对于jdk1.7加锁的范围更小,效率更高,get数据也不会加锁,而size方法是在操作元素的addCount方法中已经进行了统计
    ConcurrentHashMap原理分析(1.7与1.8)

相关文章:

  • 2022-12-23
  • 2021-04-22
  • 2022-12-23
  • 2021-08-02
  • 2021-03-31
  • 2021-12-18
  • 2022-01-06
  • 2021-05-31
猜你喜欢
  • 2021-11-30
  • 2022-01-06
  • 2021-07-26
  • 2022-01-03
  • 2021-07-19
  • 2021-04-05
  • 2021-10-23
相关资源
相似解决方案