Map基础 - 详解

DokiDoki...大约 4 分钟

Map基础 - 详解

Map 接口概览

Map接口主要存放键值对。从顺序上可以分为有序Map和无序Map,从安全性上可以分为线程安全Map和非线程安全Map。

无序Map和有序Map

  • 无序Map:HashMapHashTableConcurrentHashMap,ThreadLocalMap
  • 有序Map:TreeMapLinkedHashMapConcurrentSkipListMap

线程安全Map和线程不安全Map

  • 线程安全Map:HashTableConcurrentHashMapConcurrentSkipListMap
  • 线程不安全Map:HashMaapTreeMapThreadLocalMapLinkedHashMap

实现Map接口有哪些类?

线程安全有序性底层数据结构初始容量负载因子实例化方式
HashMap不安全无序数组+链表+红黑树160.75懒加载
HashTable安全无序数组+链表110.75初始化创建
ThreadLocalMap不安全无序数组160.75懒创建
TreeMap不安全有序(左小右大)红黑树---
LinkedHashMap不安全有序(插入顺序)数组+单双向链表---
ConcurrentHashMap安全无序数组+链表+红黑树160.75懒加载
ConcurrentSkipListMap安全有序(左小右大)跳跃表---

HashMap和HashTable的区别

  • 线程安全:HashMap线程不安全,HashTable线程安全。
  • 效率:因为HashTable内部使用了synchornized关键字进行同步,所以效率低于HashMap。(HashTable基本被淘汰,如果要使用线程安全Map推荐使用ConcurrentHashMap)
  • 有序性:HashMap和HashTable都无序。
  • Null的支持:HashMap允许Null的键值对,只能有一个Null的Key,但是允许有多个Null的Value。HashTable不允许有Null键值对。
  • 初始化:HashMap默认初始化大小为16,HashTable默认初始化大小为11。

HashMap和TreeMap 区别·

  • 有序性HashMap无序,TreeMap有序。HashMap是通过hash值进行快速查找的;HashMap中的元素是没有顺序的;TreeMap中所有的元素都是以自然顺序(升序)排序,如果需要得到一个有序的结果,就应该使用TreeMap
  • 线程安全HashMap和TreeMap线程都不安全
  • 效率:hashMap底层是数组,在添加查找删除等方面速度比较快。TreeMap底层是Tree接口,速度比较慢。但是HashMap要保存一个Array,会造成空间浪费,而TreeMap只保存要保留的节点,占用空间较小。
  • Null的支持:HashMap允许Null的键值对,只能有一个Null的Key,但是允许有多个Null的Value。TreeMap不允许有Null的Key但是允许有多个Null的Value
  • 比较键:HashMap使用Object类的equals()方法比较键。 Map类的equals()方法将其覆盖。HashTable使用compareTo()方法比较键。

HashMap与ConcurrentHashMap 的区别是什么

  • 线程安全:HashMap线程不安全,ConcurrentHashMap线程安全

ConcurrentHashMap和HashTable的区别

  • 底层数据结构:JDK1.8采用的数据结构和JDK1.8的HashMap结构一样,都是数组+链表+红黑树。HashTable和JDK1.8之前的HashMap的底层数据结构类似,都是数组+链表。链表是为了解决哈希冲突的问题。
  • 实现线程安全的方式
    • JDK1.7时,ConcurrentHashMap对整个哈希桶进行分段切割。每一段都有一把锁,多线程访问容器里不同的数据段数据的时候,就不会存在竞争,提高了并发访问率。
    • JDK1.8之后ConcurrentHashMap撅弃了分段锁的概念。直接使用数组+链表+红黑树的数据结构实现。并发控制使用synchronized和CAS来操作。
    • HashTable使用的是同一把锁,使用synchronized来保证线程安全,效率比较低下,当一个线程put元素的时候,其他线程不能put,高并发下竞争激烈,不推荐使用。

JDK1.7和JDK1.8ConcurrentHashMap有什么不同

  • 实现线程安全的方式:1.7的ConcurrentHashMap底层使用分段数组+链表实现,JDK1.8采用的数据结构和JDK1.8的HashMap结构一样,都是数组+链表+红黑树
  • 并发度:1.7之前的最大并发数为分段锁的数量(16),1.8之后最大并发数为Node数组的大小,并发数更大
  • Hash冲突解决方案:1.7采用拉链法,1.8采用拉链法+红黑树(链表和数组长度超过一定阈值后将链表转为红黑树)

HashMap长度为什么是2的幂

  • HashMap的长度为2的幂次方的原因是为了减少Hash碰撞,尽量使Hash算法的结果均匀分布
  • 扩容迁移的时候不需要再重新通过哈希定位新的位置了。扩容后,元素新的位置,要么在原脚标位,要么在原脚标位+扩容长度这么一个位置

HashMap 的 get 方法能否判断某个元素是否在 map 中

不可以,因为HashMap的Key和Value都可以为null,使用get方法并不能确定是真正意义上的null还是此元素就是null。如果要判断某个元素是否在map中,应该使用containsKey

LinkedHashMap 的实现原理

LinkedHashMap继承hashMap,可以理解为HashMap的增强体。HashMap因为底层数据结构,导致了她的无序性, LinkedHashMap使用了一对双向链表来记录添加元素的顺序。 LinkedHashMap的put存放元素其实是调用的HashMap的put方法。LinkedHashMap的重写了get()方法