集合-精讲

集合框架体系图

e7c260112c464ea0b979acd382b73fb7.png

该图片来源于互联网,博主实在难得画

我们可以从实现接口进行出发

Connection接口:

List:有序、可重复

  1. ArrayList:
    1. 底层数据结构是数组,查询快,增删慢
    2. 线程不安全、效率高
  2. Vector
    1. 底层数据结构是数组,查询快,增删慢
    2. 线程安全,但是效率低,已经舍弃
  3. LinkedList
    1. 底层结构是双向链表,查询慢,增删快
    2. 线程不安全,效率高

Set:无序,唯一

  1. HashSet
    1. 底层数据结构是哈希表。(无序,唯一)
    2. 如何来保证元素唯一性?
      1. 依赖两个方法:hashCode()和equals()
  2. LinkedHashSet
    1. 底层数据结构是链表和哈希表(FIFO插入有序,唯一)
      1. 由链表保证元素有序
      2. 由哈希表保证元素唯一
  3. TreeSet
    1. 底层数据结构是红黑树。(唯一,有序)
      1. 如何保证元素排序的呢?
        1. 自然排序
        2. 比较器排序
      2. 如何保证元素唯一性的呢?
        1. 根据比较的返回值是否是0来决定

Map接口:

HashMap:基于hash表的Map接口实现,非线程安全,高效,支持null值和null建,线程不安全

LinkedHashMap:线程不安全,是HashMap的一个子类,有序:保存了记录的插入顺序

HashTable:线程安全,低效,不支持null值和null建

TreeMap:能够把它保存的记录根据键排序,默认是键值的升序排序,线程不安全

2.5Hashmap 的底层原理(高薪常问)

JDK1.8之前的实现方式 数组+链表

JDK1.8之后:数组+链表 或者由 数组+链表或者数组+红黑树 实现,目的是提高查找效率

  1. 节点数>=8 数组+红黑树
  2. 节点数<=6数组+链表

HashMap结构

274bdfed04ee478d9adfd4c39031f661.png图片来自互联网

  1. JDK8数组+链表 OR 数组+红黑树 实现,当链表中的元素超过8个以后,会将链表转换为红黑树,当红黑树节点小于等于 6时又会退化为链表。
  2. 当new HashMap():底层没有创建数组,首次调用put()方式时,底层创建长度为16的数组,jdk8底层的数组是:Node[](节点数组)存储引用关系,而非Entry[](存储键值对),用数组容量大小乘以加载因子得到一个值,一旦数组中存储的元素个数超过该值就会调用rehash方法将数组容量增加到原来的两倍,专业术语叫做扩容,在做扩容的时候会生成一个新的数组,原来的所有数据需要重新计算哈希码值分配到新的数组,所以扩容的操作非常消耗性能
    1. 默认的负载因子大小为0.75,数组大小为16。也就是说,默认情况下,那么当HashMap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍
  3. 在我们java中任何对象都有hashcode,hash算法就是通过hashcode与自己进行向右位移16的异或运算。这样做是为了计算出来的hash值足够随机,足够分散,还有产生的数组下标足够随机。

map.put(k,v)实现原理

  1. 首先将k,v封装到Node对象当中(节点)。
  2. 先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的小标。
  3. 小标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果其中有一个equals返回了true,那么这个节点的value将会被覆盖;如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。

map.get(k)实现原理

  1. 先调用k的hashcode()方法得到k的哈希值,然后通过哈希算法转换成数组的小标
  2. 在通过数组小标快速定位到某个位置。
  3. 重点理解:如果这个位置上面什么也没有,则返回null。如果这个位置上有单向链表,那么它就会拿着参数k和单向链表上的每一个节点k进行equals,如果所有equals方法都返回false,则get方法返回null。如果某个节点返回true,就代表这个节点就是我们要找的节点,返回这个节点的value

Hash冲突

不同对象经过哈希函数得到的哈希值是一致的,这就会产生hash冲突。当单线链表达到一定长度后效率会非常低

注意:链表长度大于8的时候,就会将链表转换为红黑树,提高查询效率

Hashmap 和 hashtable ConcurrentHashMap 区别

HashMap和HashTable的区别:

  1. HashMap是非线程安全的,HashTable是线程安全的。
  2. HashMap的键和值都允许有null值存在,而HashTable则不行。
  3. HashMap线程不安全,HashTable线程安全,但是因为线程安全的原因,HashMap效率更高。
  4. HashTable是同步的,HashMap不是。因此,HashMap更适合于单线程环境,而HashTable适合于多线程环境,一般现在是不建议使用HashTable,
    1. ①是因为HashTable是遗留类,内部实现很多没有优化,并且冗余。
    2. ②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代。

HashTable和ConcurrentHashMap区别:

HashTable使用的是Synchronize关键字修饰,ConcurrentHashMap是JDK1.7使用了锁分段技术来保证线程安全的。JDK1.8ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。

ConcurrentHashMap底层是数组+链表/红黑树

HashTable底层是数组加链表

ConcurrentHashMap中的synchronized只锁定当前链表或红黑树的首节点,只要hash不冲突,就不会产生并发,效率有提示N倍

为什么ConcurrentHashMap优于HashTable?

  1. ConcurrentHashMap 在JDK1.8以后采用的是CAS 乐观锁和synchronized悲观锁,而HashTable采用的是Synchronize悲观锁。其中ConcurrentHashMap的synchronized
  2. 乐观锁的范围小于悲观锁,尤其是读多写少的场景下,并发性能优于悲观锁。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值