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
相关文章推荐
- java的hashmap与hashtable说明,简单易理解
- java的hashmap与hashtable说明,简单易理解
- HashMap & SparseArray & ArrayMap 简单说明
- ios 关于开源框架GPUImage的简单说明
- Python中numpy模块的tile()方法简单说明
- ab 测试简单说明
- fastclick.js插件使用简单说明
- java遍历HashMap简单的方法
- log的简单说明
- spring aop expression简单说明
- 【转载】用CornerStone配置SVN,HTTP及svn简单使用说明
- kernel启动流程第一阶段简单说明
- 简单说明Ubuntu Linux的区别
- Fragment 简单说明和使用方法
- (C语言版)链表(一)——实现单向链表创建、插入、删除等简单操作(包含个人理解说明及注释,新手跟着写代码)
- GPUImage.h简单说明
- 改变编译器字节对齐条件的简单说明
- Valgrind 使用简单说明
- QT中的元对象系统(一):QVariant的简单说明
- Java开发下的设计模式简单说明