java基础解析系列(三)---HashMap
来源:互联网 发布:list<map>转json 编辑:程序博客网 时间:2024/06/07 08:38
java基础解析系列(三)---HashMap
java基础解析系列
- java基础解析系列(一)---String、StringBuffer、StringBuilder
- java基础解析系列(二)---Integer
- java基础解析系列(三)---HashMap
- 这是我的博客目录,欢迎阅读
基本概念
- 节点:
Node<Key,Value>
,存放key和value
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next;}
- 键值对数组:
Node<K,V>[] table
- 加载因子
- 容量 :Node数组的长度
- 大小:hashmap存放的Node的数目
- 阈值:容量*加载因子
工作原理
- 创建一个长度为2的次幂的node数组
- put的时候,计算key的hash值,将hash值与长度-1进行与运算
- 如果数组该下标的位置为空,直接存放,如果不为空,判断节点是否为树节点,如果是的话按红黑树的方式存入,否则按照链表的形式存入
- 当hashmap的节点数目大于阈值的时候,将会重新构造hashmap,而这种操作是费时的操作,所以建议初始化一个合适的容量
域
- 默认容量,2的四次方
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
- 默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
- node 数组
transient Node<K,V>[] table;
- 键值对数目,不是table的长度
/** * The number of key-value mappings contained in this map. */ transient int size;
- 阈值
/** * The next size value at which to resize (capacity * load factor). * * @serial */ //阈值 int threshold;
- 加载因子
/** * The load factor for the hash table. * * @serial */ //加载因子 final float loadFactor;
构造方法
- 传入初始容量和加载因子
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); }
- 传入初始容量,使用默认的加载因子
public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); }
- 无参数,默认容量和加载因子
/** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
)
- 容量必须是2的n次方,当你传入的参数不符合条件,会有方法找到一个大于这个参数的最小的2的n次方数(比如大于6的最小2的n次幂是8),
put方法
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; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; 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; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
- 直接用伪代码表示
put(){ index=[hash(key)&(captity-1)]----下标的最大值为captity-1,进行与运算后最终的结果小于等于最大下标 if(table[index])==null) 直接添加node else { if(p是treenode) { 直接将节点添加到红黑树 } else { 如果不是红黑树是链表 if(p的键值==key) 覆盖value else { 遍历链表: { if(有对应的key) { 覆盖value break; } } 遍历完成后没有发现对应的key { 添加到链表 if(链表长度>8) { 将链表转化为红黑树 } } } } if(大小大于阈值) { 容量加倍,重新构造 } }}
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) { //如果链表的第一个节点是的键和要查找的键相等,那么返回该node if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; //如果不是的,看该节点是不是树节点,是的话,用树的方法查找节点,如果不是的按链表的方式查找 if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
为什么长度设置为2的n次方
if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);
- 存放node到table数组的时候,他的下标是通过(n-1)&hash计算出来的(数组长度-1 和 key的hash的值相与,最后结果小于等于长度-1),n为table的长度。
- 当长度为2的n次幂的时候,(n-1)&hash==hash%n,而前者是位运算,速度会快很多
负载因子
- 负载因子较大,说明阈值较大,也就意味着可能发生更多的冲突
- 负载因子较小,说明阈值较小,也就意味着可能会更少的冲突
- 发生冲突的时候,会降低hashmap的查找速度,所以当要求更少的内存的时候可以增加负载因子,当要求更高的查找速度的时候,可以减少负载因子。
- 默认的参数是平衡的选择,所以不建议修改
阅读全文
0 0
- java基础解析系列(三)---HashMap
- java基础解析系列(三)---HashMap
- Java基础系列---hashmap
- Java集合干货系列-(三)HashMap源码解析
- java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别
- java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别
- 深入Java集合系列之三:HashMap
- 深入Java集合系列之三:HashMap
- Java基础:HashMap和HashSet解析
- Java基础(三)HashMap源码剖析
- Java基础系列三、数组
- Java基础复习系列三
- java基础系列(三)
- Java集合源码解析(三)HashMap源码解析
- Java 集合系列10之 HashMap源码解析
- 【Java系列】(三)Java多线程---基础
- Java基础之集合框架(三)--Map、HashMap、TreeMap
- Java基础之集合框架(三)--Map、HashMap、TreeMap
- AC自动机总结
- UVA 10603 FILL
- 期望+01背包-LightOJ1079
- Nginx 配置https服务
- 一文教你迅速解决分布式事务 XA 一致性问题
- java基础解析系列(三)---HashMap
- DFS 找硬币
- 分布式版本管理工具Git
- fopen等:文本方式和二进制方式打开文件的区别
- arp协议(以太网)
- Kali Linux 渗透测试:扫描漏洞
- jquery模拟点击事件
- 【python图像处理】python中定义的颜色
- softmax分类算法原理(用python实现)