Map分支-HashMap

6 篇文章 0 订阅
先强调一点,Map不继承Collection集合类。

HashMap,key和value可以为空,非线程安全,对应线程安全的类HashTable。

类继承关系: 
extends AbstractMap<K,V> implements Map<K,V>,Cloneable,Serializable



//默认初始承载力,2的幂数,承载力就是hash的桶数
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4 ; // aka 16
//最大承载力
static final int MAXIMUM_CAPACITY = 1 << 30 ;
//默认装载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f ;
//空数组
static final Entry<? ,?>[] EMPTY_TABLE = {} ;
//数据数组
transient Entry< K , V>[] table = (Entry< K , V>[]) EMPTY_TABLE ;
//key-value个数
transient int size ;
//扩容阈值,此值=承载力*装载因子,当size大于threshold时,进行resize操作。
int threshold ;
//装载因子
final float loadFactor ;
//结构被改变次数
transient int modCount ;
// 容量阈值,默认大小为Integer.MAX_VALUE
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer. MAX_VALUE ;
/ / 计算hash值的时候需要用到
transient int hashSeed = 0 ;
//元素集合
private transient Set<Map.Entry< K , V>> entrySet = null;

//初始化hashSeed
final boolean initHashSeedAsNeeded(intcapacity) {
   boolean currentAltHashing =hashSeed!=0;
    boolean useAltHashing = sun.misc.VM.isBooted() &&
            (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    boolean switching = currentAltHashing ^ useAltHashing;
    if(switching) {
       hashSeed= useAltHashing
            ? sun.misc.Hashing.randomHashSeed(this)
            :0;
   }
   return switching;
}



//生成hash值
//一次散列,调用k的hashCode方法,与hashSeed做异或操作
//然后进行二次散列
final int hash(Object k) {
   inth =hashSeed;
    if(0!= h && kinstanceofString) {
       returnsun.misc.Hashing.stringHash32((String) k);
   }

    h ^= k.hashCode();
   // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
   h ^= (h >>> 20) ^ (h >>>12);
    returnh ^ (h >>> 7) ^ (h >>>4);
}



获取value,get方法:
public V get(Object key) {
   if(key ==null)
       return getForNullKey();
   Entry<K,V> entry = getEntry(key);

    return null== entry ? null : entry.getValue();
}



贴一下getEntry方法:
final Entry<K,V> getEntry(Object key) {
   if(size==0) {
       return null;
   }

   inthash = (key ==null) ?0: hash(key);
    for(Entry<K,V> e = table[indexFor(hash,table.length)];
        e !=null;
        e = e.next) {
        Object k;
        if(e.hash== hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
           return e;
   }
   return null;
}


大致过程:获得key值的hash值,通过hash值找到table的index索引值,遍历这条链,找到与key相同的元素。

这里说一下HashMap的结构,其实HashMap就是一个数组,代码用table表示,每个元素其实又是一个链表的首节点。就是常说的链表法,解决hash冲突,相同hash值的元素,加入在对应位置的链表尾部。如图



put方法:
publicV put(K key,V value) {
   if(table==EMPTY_TABLE) {
        inflateTable(threshold);
   }
   if(key ==null)
       return putForNullKey(value);
    int hash = hash(key);
    int i =indexFor(hash,table.length);
    for(Entry<K,V> e = table[i];e !=null;e = e.next) {
        Object k;
        if(e.hash== hash && ((k = e.key) == key || key.equals(k))) {
           V oldValue = e.value;
           e.value= value;
           e.recordAccess(this);
           return oldValue;
       }
    }

   modCount++;
   addEntry(hash,key,value,i);
    return null;
}



//扩容table
private void inflateTable(int toSize) {
   // Find a power of 2 >= toSize
   int capacity = roundUpToPowerOf2(toSize);

   threshold = (int) Math.min(capacity * loadFactor,MAXIMUM_CAPACITY + 1);
   table = new Entry[capacity];
   initHashSeedAsNeeded(capacity);
}



//添加元素
void addEntry(int hash,K key,V value, intbucketIndex) {
   if((size>=threshold) && (null!=table[bucketIndex])) {
        resize(2*table.length);
       hash = (null!= key) ? hash(key) :0;
       bucketIndex =indexFor(hash,table.length);
   }

    createEntry(hash,key,value,bucketIndex);
}



这里发现了几个点:
1.无参构造函数(我们常用的new HashMap),只是设置了扩容阈值为 DEFAULT_INITIAL_CAPACITY(16),table默认是空数组。当put元素的时候,先进行一次扩容,承载力扩大到32(hash桶数),扩容阈值为32*0.75(默认)=24
2.添加元素,扩容的条件不只是size>=threshold扩容阈值,还需要 bucketIndex索引位置是null,意思就是 bucketIndex位置还没有元素

旧表转新表的代码:
void transfer(Entry[] newTable, booleanrehash) {
   int newCapacity = newTable.length;
    for(Entry<K,V> e : table) {
       while(null!= e) {
            Entry<K,V> next = e.next;
            if(rehash) {
                e.hash=null== e.key?0: hash(e.key);
           }
           inti =indexFor(e.hash,newCapacity);
           e.next= newTable[i];
           newTable[i] = e;
           e = next;
       }
    }
}


每个index上的链表,从表头开始一个一个添加到新表中。皓哥说并发下会出现死循环,具体可以看下皓哥的博客。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值