您的位置:首页 > 其它

HashMap简单说明

2017-09-15 11:25 281 查看


存储结构

初始化
```
// 初始化数组大小为默认大小
Map<String, String> map = new HashMap<>();

/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
```


HashMap是
链表散列
数据结构, 通过使用
数组
链表
组合方式实现快速定位,
快速修改.

数组(table): 查找方便, 修改困难

链表(Entry<K,V>): 查找困难, 修改方便
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry<K,V>[] table;

static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
...
}

}


简单示意图

保存数据分为两种情况
key == null
总是保存在数组第一个位置(Entry<K,V> e = table[0]), 如果数组第一个位置桶已经存在, 找到第一个key == null的节点, 使用新值覆盖原来的值。 如果没有就创建一个Entry节点,并在数组中保存对节点的引用。
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
...
}

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

key != null
首先计算key的hash值, 然后根据hash值找到桶的位置 ,
hash & (table.length-1)
, 由于table.length总是2的倍数,
该操作与 
hash % table.length
 结果一致 ,再找到桶( 链表 )中与key一致的节点 if (e.hash == hash && ((k = e.key) == key || key.equals(k))).
如果找到, 就替换掉原来的值 e.value = value.
如果没找到就在该桶的位置创建新的节点, 并把原来已存在的数据放在新节点之后 table[bucketIndex] = new Entry<>(hash, key, value, table[bucketIndex]).
map.put(0, "0");

public V put(K key, V value) {

...

// 计算hash值
int hash = hash(key);

// 通过hash值找到桶的位置
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);

...
}

注意: 值覆盖/修改不会改变modCount, 也就是说影响视图迭代器(View Iterator)的使用, 而通过HashMap.put/HashMap.remove会导致视图迭代器失效, 并抛出异常ConcurrentModificationException
private abstract class HashIterator<E> implements Iterator<E> {
...
HashIterator() {
expectedModCount = modCount;
...
}

final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
...
}
...
}


获取数据
首先计算key的hash值, null总是在第一个桶 int hash = (key == null) ? 0 : hash(key) , 再找到与key一致的节点 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}

int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}

这也导致覆盖一个对象的equals方法时, 需要同时覆盖hashCode方法的原因
以上仅个人观点, 欢迎留言讨论.

原文链接: http://note.youdao.com/noteshare?id=a3ef96e24994e6f931bdbbd5962ef7f1&sub=5880B496A13F469BAED5BA98175CF140
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: