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
);
}
}