HashMap源码分析

1 构造方法
HashMap 中有四个构造方法,它们分别如下:
this . key = key ;
this . value = value ;
this . next = next ;
}
public final K getKey () { return key ; }
public final V getValue () { return value ; }
public final String toString () { return key + "=" + value ; }
// 重写 hashCode() 方法
public final int hashCode () {
return Objects . hashCode ( key ) ^ Objects . hashCode ( value );
}
public final V setValue ( V newValue ) {
V oldValue = value ;
value = newValue ;
return oldValue ;
}
// 重写 equals() 方法
public final boolean equals ( Object o ) {
if ( o == this )
return true ;
if ( o instanceof Map . Entry ) {
Map . Entry <? , ?> e = ( Map . Entry <? , ?> ) o ;
if ( Objects . equals ( key , e . getKey ()) &&
Objects . equals ( value , e . getValue ()))
return true ;
}
return false ;
}
}
static final class TreeNode < K , V > extends LinkedHashMap . Entry < K , V > {
TreeNode < K , V > parent ; //
TreeNode < K , V > left ; //
TreeNode < K , V > right ; //
TreeNode < K , V > prev ; // needed to unlink next upon deletion
boolean red ; // 判断颜色
TreeNode ( int hash , K key , V val , Node < K , V > next ) {
super ( hash , key , val , next );
}
// 返回根节点
final TreeNode < K , V > root () {
for ( TreeNode < K , V > r = this , p ;;) {
if (( p = r . parent ) == null )
return r ;
r = p ;
}
// 默认构造函数。 putMapEntries 方法:
2 put 方法
public HashMap () {
this . loadFactor = DEFAULT_LOAD_FACTOR ; // all other fields defaulted
}
// 包含另一个 “Map” 的构造函数
public HashMap ( Map <? extends K , ? extends V > m ) {
this . loadFactor = DEFAULT_LOAD_FACTOR ;
putMapEntries ( m , false ); // 下面会分析到这个方法
}
// 指定 容量大小 的构造函数
public HashMap ( int initialCapacity ) {
this ( initialCapacity , DEFAULT_LOAD_FACTOR );
}
// 指定 容量大小 加载因子 的构造函数
public HashMap ( int initialCapacity , float loadFactor ) {
if ( initialCapacity < 0 )
throw new IllegalArgumentException ( "Illegal initial capacity: " +
initialCapacity );
if ( initialCapacity > MAXIMUM_CAPACITY )
initialCapacity = MAXIMUM_CAPACITY ;
if ( loadFactor <= 0 || Float . isNaN ( loadFactor ))
throw new IllegalArgumentException ( "Illegal load factor: " +
loadFactor );
this . loadFactor = loadFactor ;
this . threshold = tableSizeFor ( initialCapacity );
}
final void putMapEntries ( Map <? extends K , ? extends V > m , boolean evict ) {
int s = m . size ();
if ( s > 0 ) {
// 判断 table 是否已经初始化
if ( table == null ) { // pre-size
// 未初始化, s m 的实际元素个数
float ft = (( float ) s / loadFactor ) + 1.0F ;
int t = (( ft < ( float ) MAXIMUM_CAPACITY ) ?
( int ) ft : MAXIMUM_CAPACITY );
// 计算得到的 t 大于阈值,则初始化阈值
if ( t > threshold )
threshold = tableSizeFor ( t );
}
// 已初始化,并且 m 元素个数大于阈值,进行扩容处理
else if ( s > threshold )
resize ();
// m 中的所有元素添加至 HashMap
for ( Map . Entry <? extends K , ? extends V > e : m . entrySet ()) {
K key = e . getKey ();
V value = e . getValue ();
putVal ( hash ( key ), key , value , false , evict );
}
}
} HashMap 只提供了 put 用于添加元素, putVal 方法只是给 put 方法调用的一个方法,并没有提供给用户 使用。
putVal 方法添加元素的分析如下:
①如果定位到的数组位置没有元素 就直接插入。
②如果定位到的数组位置有元素就和要插入的 key 比较,如果 key 相同就直接覆盖,如果 key 不相
同,就判断 p 是否是一个树节点,如果是就调用 e = ((TreeNode<K,V>)p).putTreeVal(this,
tab, hash, key, value) 将元素添加进入。如果不是就遍历链表插入 ( 插入的是链表尾部 )
 
 
 
public V put ( K key , V value ) {
return putVal ( hash ( key ), key , value , false , true );
}
final V putVal ( int hash , K key , V value , boolean onlyIfAbsent ,
boolean evict ) {
Node < K , V > [] tab ; Node < K , V > p ; int n , i ;
// table 未初始化或者长度为 0 ,进行扩容
if (( tab = table ) == null || ( n = tab . length ) == 0 )
n = ( tab = resize ()). length ;
// (n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中 ( 此时,这个结点是放
在数组中 )
if (( p = tab [ i = ( n - 1 ) & hash ]) == null )
tab [ i ] = newNode ( hash , key , value , null );
// 桶中已经存在元素
else {
Node < K , V > e ; K k ;
// 比较桶中第一个元素 ( 数组中的结点 ) hash 值相等, key 相等
if ( p . hash == hash &&
(( k = p . key ) == key || ( key != null && key . equals ( k ))))
// 将第一个元素赋值给 e ,用 e 来记录
e = p ;
// hash 值不相等,即 key 不相等;为红黑树结点
else if ( p instanceof TreeNode ) // 放入树中
e = (( TreeNode < K , V > ) p ). putTreeVal ( this , tab , hash , key , value );
// 为链表结点
else {
// 在链表最末插入结点
for ( int binCount = 0 ; ; ++ binCount ) {
// 到达链表的尾部
if (( e = p . next ) == null ) {
// 在尾部插入新结点
p . next = newNode ( hash , key , value , null );
// 结点数量达到阈值,转化为红黑树
if ( binCount >= TREEIFY_THRESHOLD - 1 ) // -1 for 1st
treeifyBin ( tab , hash );
// 跳出循环
break ;
}
// 判断链表中结点的 key 值与插入的元素的 key 值是否相等
if ( e . hash == hash &&
(( k = e . key ) == key || ( key != null && key . equals ( k ))))
// 相等,跳出循环
break ;
// 用于遍历桶中的链表,与前面的 e = p.next 组合,可以遍历链表
p = e ;
}
}
// 表示在桶中找到 key 值、 hash 值与插入元素相等的结点
if ( e != null ) {
// 记录 e value
V oldValue = e . value ;
// onlyIfAbsent false 或者旧值为 null
if ( ! onlyIfAbsent || oldValue == null )
// 用新值替换旧值
e . value = value ;
// 访问后回调
afterNodeAccess ( e );
// 返回旧值
return oldValue ;
}
}
// 结构性修改
++ modCount ;
// 实际大小大于阈值则扩容
if ( ++ size > threshold )
resize ();
// 插入后回调
afterNodeInsertion ( evict );
return null ;
}
我们再来对比一下 JDK1.7 put 方法的代码
对于 put 方法的分析如下:
①如果定位到的数组位置没有元素 就直接插入。
②如果定位到的数组位置有元素,遍历以这个元素为头结点的链表,依次和插入的 key 比较,如果
key 相同就直接覆盖,不同就采用头插法插入元素。
public V 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 ;
}
3 get 方法
public V get ( Object key ) {
Node < K , V > e ;
return ( e = getNode ( hash ( key ), key )) == null ? null : e . value ;
}
final Node < K , V > getNode ( int hash , Object key ) {
Node < K , V > [] tab ; Node < K , V > first , e ; int n ; K k ;
if (( tab = table ) != null && ( n = tab . length ) > 0 &&
( first = tab [( n - 1 ) & hash ]) != null ) {
// 数组元素相等
if ( first . hash == hash && // always check first node
(( k = first . key ) == key || ( key != null && key . equals ( k ))))
return first ;
// 桶中不止一个节点
if (( e = first . next ) != null ) {
// 在树中 get
if ( first instanceof TreeNode )
return (( TreeNode < K , V > ) first ). getTreeNode ( hash , key );
// 在链表中 get
do {
if ( e . hash == hash &&
(( k = e . key ) == key || ( key != null && key . equals ( k ))))
return e ;
} while (( e = e . next ) != null );
}
}
return null ;
}
4 resize 方法
进行扩容,会伴随着一次重新 hash 分配,并且会遍历 hash 表中所有的元素,是非常耗时的。在编写程 序中,要尽量避免resize
final Node < K , V > [] resize () {
Node < K , V > [] oldTab = table ;
int oldCap = ( oldTab == null ) ? 0 : oldTab . length ;
int oldThr = threshold ;
int newCap , newThr = 0 ;
if ( oldCap > 0 ) {
// 超过最大值就不再扩充了,就只好随你碰撞去吧
if ( oldCap >= MAXIMUM_CAPACITY ) {
threshold = Integer . MAX_VALUE ;
return oldTab ;
}
// 没超过最大值,就扩充为原来的 2
else if (( newCap = oldCap << 1 ) < MAXIMUM_CAPACITY && oldCap >=
DEFAULT_INITIAL_CAPACITY )
newThr = oldThr << 1 ; // double threshold
}
else if ( oldThr > 0 ) // initial capacity was placed in threshold
newCap = oldThr ;
else {
// signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY ;
newThr = ( int )( DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY );
}
// 计算新的 resize 上限
if ( newThr == 0 ) {
float ft = ( float ) newCap * loadFactor ;
newThr = ( newCap < MAXIMUM_CAPACITY && ft < ( float ) MAXIMUM_CAPACITY ?
( int ) ft : Integer . MAX_VALUE );
}
threshold = newThr ;
@SuppressWarnings ({ "rawtypes" , "unchecked" })
Node < K , V > [] newTab = ( Node < K , V > []) new Node [ newCap ];
table = newTab ;
if ( oldTab != null ) {
// 把每个 bucket 都移动到新的 buckets
for ( int j = 0 ; j < oldCap ; ++ j ) {
Node < K , V > e ;
if (( e = oldTab [ j ]) != null ) {
oldTab [ j ] = null ;
if ( e . next == null )
newTab [ e . hash & ( newCap - 1 )] = e ;
else if ( e instanceof TreeNode )
(( TreeNode < K , V > ) e ). split ( this , newTab , j , oldCap );
else {
Node < K , V > loHead = null , loTail = null ;
Node < K , V > hiHead = null , hiTail = null ;
Node < K , V > next ;
do {
next = e . next ;
// 原索引
if (( e . hash & oldCap ) == 0 ) {
if ( loTail == null )
loHead = e ;
else
loTail . next = e ;
loTail = e ;
}
// 原索引 +oldCap
else {
if ( hiTail == null )
hiHead = e ;
else
hiTail . next = e ;
hiTail = e ;
}
} while (( e = next ) != null );
// 原索引放到 bucket
if ( loTail != null ) {
loTail . next = null ;
newTab [ j ] = loHead ;
}
// 原索引 +oldCap 放到 bucket
if ( hiTail != null ) {
hiTail . next = null ;
newTab [ j + oldCap ] = hiHead ;
}
}
}
}
}
return newTab ;
}
5 HashMap 常用方法测试
package map ;
import java . util . Collection ;
import java . util . HashMap ;
import java . util . Set ;
public class HashMapDemo {
public static void main ( String [] args ) {
HashMap < String , String > map = new HashMap < String , String > ();
// 键不能重复,值可以重复
map . put ( "san" , " 张三 " );
map . put ( "si" , " 李四 " );
map . put ( "wu" , " 王五 " );
map . put ( "wang" , " 老王 " );
map . put ( "wang" , " 老王 2" ); // 老王被覆盖
map . put ( "lao" , " 老王 " );
System . out . println ( "------- 直接输出 hashmap:-------" );
System . out . println ( map );
/**
* 遍历 HashMap
*/
// 1. 获取 Map 中的所有键
System . out . println ( "-------foreach 获取 Map 中所有的键 :------" );
Set < String > keys = map . keySet ();
for ( String key : keys ) {
System . out . print ( key + " " );
}
System . out . println (); // 换行
// 2. 获取 Map 中所有值
System . out . println ( "-------foreach 获取 Map 中所有的值 :------" );
Collection < String > values = map . values ();
for ( String value : values ) {
System . out . print ( value + " " );
}
System . out . println (); // 换行
// 3. 得到 key 的值的同时得到 key 所对应的值
System . out . println ( "------- 得到 key 的值的同时得到 key 所对应的值 :-------" );
Set < String > keys2 = map . keySet ();
for ( String key : keys2 ) {
System . out . print ( key + " " + map . get ( key ) + " " );
}
/**
* 如果既要遍历 key 又要 value ,那么建议这种方式,应为如果先获取 keySet 然后再执行
map.get(key) map 内部会执行两次遍历。
* 一次是在获取 keySet 的时候,一次是在遍历所有 key 的时候。
*/
// 当我调用 put(key,value) 方法的时候,首先会把 key value 封装到
// Entry 这个静态内部类对象中,把 Entry 对象再添加到数组中,所以我们想获取
// map 中的所有键值对,我们只要获取数组中的所有 Entry 对象,接下来
// 调用 Entry 对象中的 getKey() getValue() 方法就能获取键值对了
Set < java . util . Map . Entry < String , String >> entrys = map . entrySet ();
for ( java . util . Map . Entry < String , String > entry : entrys ) {
System . out . println ( entry . getKey () + "--" + entry . getValue ());
}
/**
* HashMap 其他常用方法
*/
System . out . println ( "after map.size() " + map . size ());
System . out . println ( "after map.isEmpty() " + map . isEmpty ());
System . out . println ( map . remove ( "san" ));
System . out . println ( "after map.remove() " + map );
System . out . println ( "after map.get(si) " + map . get ( "si" ));
System . out . println ( "after map.containsKey(si) " + map . containsKey ( "si" ));
System . out . println ( "after containsValue( 李四 ) " + map . containsValue ( "
" ));
System . out . println ( map . replace ( "si" , " 李四 2" ));
System . out . println ( "after map.replace(si, 李四 2):" + map );
}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值