AbstractMap
Map的内部实现,key用一个set实现, value用一个Collection
1
2
transient volatile Set < K > keySet = null ;
transient volatile Collection < V > values = null ;
HashMap
内部就一个Entry数组,有一个装载因子的概念
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//默认容量
static final int DEFAULT_INITIAL_CAPACITY = 16 ;
//默认装载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f ;
//长度必须为2的幂次方
transient Entry [] table ;
//阈值, = capacity * load factor, 当size达到这个值的时候需要搬家
int threshold ;
//装载因子
final float loadFactor ;
而Entry对象包含key,value,当前节点的hash值以及一下个Entry对象的指针
1
2
3
4
5
6
static class Entry < K , V > implements Map . Entry < K , V > {
final K key ;
V value ;
Entry < K , V > next ;
final int hash ;
}
hash函数及下标:
1
2
3
4
5
6
7
8
9
10
11
12
13
//hash函数, h为key.hashCode()值
static int hash ( int h ) {
// 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 );
return h ^ ( h >>> 7 ) ^ ( h >>> 4 );
}
//通过hash()的返回值,获取在桶中的下标
static int indexFor ( int h , int length ) {
return h & ( length - 1 );
}
HashMap在多线程环境下会出现的死循环问题,详细 .
有人把这个问题报给了Sun,不过Sun不认为这个是一个问题。因为HashMap本来就不支持并发。要并发就用ConcurrentHashmap
,详见 .由此也可见,HashMap不是线程安全的.
HashTable
HashTable不允许value为null, 而HashMap允许.
默认Capacity为11, 负载因子0.75
1
2
3
public Hashtable () {
this ( 11 , 0.75f );
}
通过key找到其在桶中下标
1
2
int hash = key . hashCode ();
int index = ( hash & 0x7FFFFFFF ) % tab . length ;
HashTable线程安全的,是通过每个方法都加上synchronized关键字.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public synchronized V get ( Object key ) {
Entry tab [] = table ;
int hash = key . hashCode ();
int index = ( hash & 0x7FFFFFFF ) % tab . length ;
for ( Entry < K , V > e = tab [ index ] ; e != null ; e = e . next ) {
if (( e . hash == hash ) && e . key . equals ( key )) {
return e . value ;
}
}
return null ;
}
public synchronized V put ( K key , V value ) {
...
}
IdentityHashMap
在IdentityHashMap中,判断两个键值k1和 k2相等的条件是 k1 == k2 。在正常的Map 实现(如 HashMap)中,当且仅当满足下列条件时才认为两个键 k1 和 k2 相等:(k1==null ? k2==null : e1.equals(e2))。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public boolean containsKey ( Object key ) {
Object k = maskNull ( key );
Object [] tab = table ;
int len = tab . length ;
int i = hash ( k , len );
while ( true ) {
Object item = tab [ i ];
if ( item == k ) //判断键值相等
return true ;
if ( item == null )
return false ;
i = nextKeyIndex ( i , len );
}
}
简单示例:
1
2
3
4
5
IdentityHashMap < String , Object > map = newIdentityHashMap < String , Object >();
//这两个entry会同时存在, 因为两次new String("xx")地址不一样
map . put ( new String ( "xx" ), "first" );
map . put ( new String ( "xx" ), "second" );