你所忽略的HashMap
为什么突然之间要说HashMap呢,是因为最近一个学长问了我许多问题,才发现我在学知识的时候思考的真的是太少 了,因此我想要改变这种方式,接下来让我们说说HashMap吧
1、首先呢,先了解下HashMap
关于HashMap的方法
对于HashMap有了基本的了解,那么我们来谈谈HashMap中的key和value,相信很多人都知道也使用过当value为null,那么key是否又为空呢?
答案是key和value都可为空,各自的用法又有什么作用?等等之类的问题
关于HashMap中的使用,我觉得有篇博客写的不错HashMap的详解
1、key 和value为什么可以为空?key为空是放在哪里?
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
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;
}
*/
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
private void putForCreate(K key, V value) {
int hash = (key == null) ? 0 : hash(key.hashCode());
int i = indexFor(hash, table.length);
/**
* Look for preexisting entry for key. This will never happen for
* clone or deserialize. It will only happen for construction if the
* input Map is a sorted map whose ordering is inconsistent w/ equals.
*/
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
e.value = value;
return;
}
}
createEntry(hash, key, value, i);
}
以上是HashMap中源码的一部分,这也是为什么key和value为什么为空,(PS:如果在问我一次,我觉得我会傻逼的回答,在HashMap源码中定义了为空时的用法,这也是为什么为空,有点傻,不要介意)。
key为空时是放在数组下标为空的地方
2、当key不为空时,怎么样存储?
当我们在HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(及下标),如果数组位置上已经存放有其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将元素放在次数组中的该位置上。
final Entry<K,V> removeEntryForKey(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
3、key的默认值是多少?为什么是这么多?
这个问题,个人觉得看源码吧
//key的默认大小是16
static final int DEFAULT_INITIAL_CAPACITY = 16;
//为什么是16
/*按照位运算,刚好是求余操作,也就是2的整数次幂方*/
static int indexFor(int h, int length) {
return h & (length-1);
}
4、HashMap什么时候扩容?
HashMap中有个加载因子,当加载因子超过0.75时就会扩容。
原话是这样的:
HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量翻倍。
通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地降低 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
以上是我的一些理解,如果有更好的理解,欢迎评论!