HashMap相信大家都用过,是以<key,value>这样的格式存储的。其实内部真正用于存储的是entry的数组table(桶)。,下面就是源代码了已经标注出来了
emprty_table是个空表,用于是初始化时使用的。default_load_factor是负载因子,default_initial_capacity是初始化容量,maximum_capacity是最大容量是2^30=1073741824.
size才是你存储的容量大小。
Integer.hightestOneBit(int)是将给定参数的最高位的1保留,剩下的变为0的操作,简单说就是将参数int变为小于等于它的最大的2的n次幂。
讲一下entry的结构,有个key,value和对应下一个entry对象next和hash值,类似链表结构。
接下来讲解一下hashmap的最为重要的取值get(key),存储put(key,value)的内容,在这之前,hashmap并不是直接将key作为桶table的下标,而是经过计算hash(key)=hashcode,在拿hashcode进行函数indexFor()计算出对应index。接下来先讲一下get的过程。
1.get在取值的过程中,先进性判断key是否为null,如果为null,直接定位到index=0的链表列,然后做循环查找key为null的value,如果key不为空,进行hash(key),indexFor()运算得到下标i,计算出对应的index=i的链表列,然后循环查找此链表列key等同的value,找不到返回null。贴一下源代码
2.put的存储值的过程是,(空table扩容我就不说了),判断key是否为null,如果为null,直接定位到index=0的链表列,直接然后做循环查找key为null的对象entry,如果entry对象存在就进行更新value,如果不存在,就创建entry对象,然后将table[0]的链表作为entry的next;如果key不为空,进行hash(key),indexFor()运算得到下标i,计算出对应的index=i的链表列,然后循环查找此链表列key等同的value,如果entry对象存在就进行更新value,如果不存在,就创建entry对象,然后将table[i]的链表作为entry的next。下面是对应的源码。
添加entry的方法源码:
大概的主要方法讲完了。
但是还有个关键点就是hashmap不是线程安全的,在多线程的环境下,可能出现key对应的value的不一致的。
那在多线程下使用HashMap我们需要怎么做,几种方案:
- 在外部包装HashMap,实现同步机制
- 使用Map m = Collections.synchronizedMap(new HashMap(...));,这里就是对HashMap做了一次包装
- 使用java.util.HashTable,效率最低
- 使用java.util.concurrent.ConcurrentHashMap,相对安全,效率较高
第一中就不讲了,可以增加对应sunchronized进行同步代码块操作,稍微讲一下其他三个方法吧。
1. Map map= Collections.synchronizedMap(new HashMap());
这里做的操作就是调用了collections里面synchronizedMap()的方法(里面还有好多其他的list,set等方法),返回了一个一个新的对象SynchronizedMap,此类为collections的内部类,此内容类会对对应的map对象的关键的方法进行同步封装,使其是同步的。
2.hashable也是采用了进行方法包装加上synchronized ,hashtable与hashmap继承不同的类,hashtable继承dictionary类,而hashmap继承abstractmap类,hashtable不允许空值key or value ,而hashmap是允许一个key为null,多个null 的value。
3.concurrentmap是用了更为新颖的思想,没用用entry对象而是使用了segment对象,而segment继承了可重入锁。
ConcurrentHashMap为了提高本身的并发能力,在内部采用了一个叫做Segment的结构,一个Segment其实就是一个类Hash Table的key ,value结构,Segment内部维护了一个链表数组.
HashMap是非线程安全的,Hashtable是线程安全的,但是由于Hashtable是采用synchronized进行同步,相当于所有线程进行读写时都去竞争一把锁,导致效率非常低下。ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,不用对整个ConcurrentHashMap加锁。concurrentmap的内容到时候具体再详解了。