HashMap、HashTable和ConcurrentHashMap的区别

HashTableHashMap
继承父类的不同DictionaryAbstractMap
都同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口都同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口
默认容量1116
Table的初始化时机构造函数中初始化第一次put
并发操作使用同步机制,
实际应用程序中,仅仅是Hashtable本身的同步并不能保证程序在并发操作下的正确性,需要高层次的并发保护。
下面的代码试图在key所对应的value值等于x的情况下修改value为x+1
{
value = hashTable.get(key);
if(value.intValue()== x){
hashTable.put(key, new Integer(value.intValue()+1));
}
}
如2个线程同时执行以上代码,可能放入不是x+1,而是x+2.
没有同步机制,需要使用者自己进行并发访问控制
数据遍历的方式Iterator 和 EnumerationIterator
是否支持fast-fail用Iterator遍历,支持fast-fail
用Enumeration不支持fast-fail.
支持fast-fail
是否接受值为null的Key 或Value?不接受接收
根据hash值计算数组下标的算法当数组长度较小,并且Key的hash值低位数值分散不均匀时,不同的hash值计算得到相同下标值的几率较高
hash = key.hashCode();
index=(hash&0x7FFFFFFF) % tab.length;
//去掉其符号位置
优于hashtable,通过对Key的hash做移位运算和位的与运算,使其能更广泛地分散到数组的不同位置
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
hash = hash (k);
index = indexFor(hash, table.length);
static int hash(Object x) {
int h = x.hashCode();
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}把后面几位移动到前面
static int indexFor(int h, int length) {
return h & (length-1);
}
Entry数组的长度缺省初始长度为11,初始化时可以指定initial capacity缺省初始长度为16,长度始终保持2的n次方初始化时可以指定initial capacity,若不是2的次方,HashMap将选取第一个大于initial capacity 的2n次方值作为其初始长度
LoadFactor负荷因子0.750.75
负荷超过(loadFactor * 数组长度)时,内部数据的调整方式扩展数组:2*原数组长度+1扩展数组: 原数组长度 * 2
两者都会重新根据Key的hash值计算其在数组中的新位置,重新放置。算法相似,时间、空间效率相同两者都会重新根据Key的hash值计算其在数组中的新位置,重新放置。算法相似,时间、空间效率相同


ConcurrentHashMap

继承关系

讲解的并发集合源码是基于JDK 1.7实现讲解
ConcurrentHashMap的继承关系如下:
在这里插入图片描述
通过上图可知,ConcurrentHashMap是属于ConcurrentMap接口,而ConcurrentMap接口又是Map接口的子接口,ConcurrentHashMap是实现了Map提供的所有方法

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
    implements ConcurrentMap<K, V>, Serializable 

ConcurrentMap接口提供方法如下:

public interface ConcurrentMap<K, V> extends Map<K, V> {
    //如果指定键已经不再与某个值相关联,则将它与给定值关联  ->key不存在该集合中,将进行put插入操作,key存在则直接返回
    V putIfAbsent(K key, V value);

    //只有目前将键的条目映射到给定值时,才移除该键的条目 ->key和value键值对同时存在则删除,否则不进行操作
    boolean remove(Object key, Object value);

    //只有目前将键的条目映射到给定值时,才替换该键的条目 ->key和oldvalue键值对存在时,才替换为key和newvalue键值对
    boolean replace(K key, V oldValue, V newValue);

    //只有目前将键的条目映射到某一值时,才替换该键的条目 ->key存在集合中,才进行key和value键值对的更新
    V replace(K key, V value);
}
构造函数

在这里插入图片描述
ConcurrentHashMap提供了5个构造函数,主要的构造函数是ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel)
其中initialCapacity叫做初始化容量,不是整个COncurrenthashmap的容量,而是table属性的初始数组容量,大于给定值的最小的2的幂
loadFactor是加载因子,
concurrencyLevel是并发度,指的是外层数组的大小,即segments数组的大小,大于给定值的最小的2的幂

public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
    //参数校验
        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (concurrencyLevel > MAX_SEGMENTS)
            concurrencyLevel = MAX_SEGMENTS;
        // Find power-of-two sizes best matching arguments
        int sshift = 0;
        int ssize = 1;
        while (ssize < concurrencyLevel) {
            ++sshift;
            ssize <<= 1;  
        }
        this.segmentShift = 32 - sshift;   
        this.segmentMask = ssize - 1;
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
  
        int c = initialCapacity / ssize; 
                
            
        if (c * ssize < initialCapacity)
            ++c;
        int cap = MIN_SEGMENT_TABLE_CAPACITY;
        while (cap < c)
            cap <<= 1;
        // create segments and segments[0]
        Segment<K,V> s0 =
            new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                             (HashEntry<K,V>[])new HashEntry[cap]);
        Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize]; /4
        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
        this.segments = ss;
    }

内部类Segment声明

static final class Segment<K,V> extends ReentrantLock implements Serializable 

Segment是继承ReentrantLock类,该类是重入锁

属性和默认值
//默认的初始容量  默认16
static final int DEFAULT_INITIAL_CAPACITY = 16;

   默认的加载因子 0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

   默认的并发度
    static final int DEFAULT_CONCURRENCY_LEVEL = 16;

   容量的最大值  2^30    
    static final int MAXIMUM_CAPACITY = 1 << 30;

   最小的segment的表容量 2
    static final int MIN_SEGMENT_TABLE_CAPACITY = 2;

    最大的segment的容量 2^16
    static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
    
   锁重试次数2
    static final int RETRIES_BEFORE_LOCK = 2;

  数据存储在segment数组中
 final Segment<K,V>[] segments;


static final class Segment<K,V> {
     
transient volatile HashEntry<K,V>[] table //数据存储在table属性中
 }

 class HashEntry<K,V> {
        final int hash;
        final K key;
        volatile V value;
        volatile HashEntry<K,V> next;
 }

底层的数据结构是数据+Hash表结构
ConcurrentHashMap存储数据在segments属性上,该属性是一个Segment数组,该数组存储都是Segment类型的数据,而Segment类中存储数据在table属性上,table属性是HashEntry类型的数组,其数据通过数组加链表来存储数据,具体存储格式示意图如下:
在这里插入图片描述

ConcurrenthashMap的特点

1、ConcurrentHashMap的扩容针对是是table扩容,segment不进行扩容
2、ConcurrenthashMap的扩容是原table大小的2倍
3、ConcurrentHashMap中key和value都不能为空
4、ConcurrentHashMap的加锁针对的是每一个Segment加锁(tryLock(), unlock())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值