首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
第一章:集合概述
第二章:Collection 接口
2.1 概述
2.2 常用方法
第三章:Iterator 迭代器
3.1 Iterator 接口
3.2 迭代器实现原理
3.3 使用 Iterator 迭代器删除元素
3.4 并发修改异常
3.5 集合存储自定义对象并迭代
3.6 增强 for 循环
3.7 java.lang.Iterable 接口
第四章:List 接口
4.1 概述
4.2 常用方法
4.3 List 特有的迭代器 ListIterator
4.4 List 接口的实现类:ArrayList
4.5 List 接口的实现类:LinkedList
第五章:Set 接口
5.1 概述
5.2 Set 的实现类:HashSet
5.3 Set 的实现类之三:TreeSet
第六章:Collections 工具类
6.1 概述
6.2 常用方法
6.3 Collections 的同步控制方法
第七章:Map 接口
7.1 概述
Map 接口常用的方法
7.2 Map 的实现类:HashMap
7.3 Map 的实现类:LinkedHashMap
7.4 Map 的实现类:Hashtable
7.5 Map 的实现类:TreeMap
7.6 Map 的实现类:Properties
当前位置:
首页>>
技术小册>>
Java语言基础10-Java中的集合
小册名称:Java语言基础10-Java中的集合
7.2.1 概述 - HashMap 是 Map 接口使用频率较高的实现类。 - 允许使用 null 键和 null 值,和 HashSet 一样,不能保证映射的顺序。 - 所有的 key 构成的集合是 Set:不重复的。所以,key 所在的类需要重写 equals() 和 hashCode() 方法。 - 所有的 value 构成的集合是 Collection:可以重复的。所以,value 所在的类需要重写 equals() 方法。 - 一个 key - value 构成一个 entry 。 - HashMap 判断两个 key 相等的标准:两个 key 的 hashCode() 和 equals() 分别都相等。 - HashMap 判断两个 value 相等的标准:两个 value 的 equals() 相等。 72.2 HashMap 的成员变量 - 哈希表数组的初始化容量: ``` static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 ``` - 哈希表数组的最大容量: ``` static final int MAXIMUM_CAPACITY = 1 << 30; ``` - 默认的加载因子: ``` static final float DEFAULT_LOAD_FACTOR = 0.75f; ``` - 阈值(转红黑树): ``` static final int TREEIFY_THRESHOLD = 8; ``` - 阈值(解除红黑树): ``` static final int UNTREEIFY_THRESHOLD = 6; ``` - 阈值(转红黑树): ``` static final int MIN_TREEIFY_CAPACITY = 64; ``` 7.2.3 HashMap 的内部类Node ``` public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { // Node节点 static class Node<K,V> implements Map.Entry<K,V> { final int hash; // 对象的hash值 final K key; // 存储对象 V value; // 使用Set的时候,是Object的对象 Node<K,V> next; // 链表的下一个节点 Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; 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; } public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } 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; } } ... } ``` 7.2.4 HashMap 的 put 方法 ``` // HashMap存储对象的方法 public V put(K key, V value) { // 存储值 参数:传递新计算的hash值,要存储的元素 return putVal(hash(key), key, value, false, true); } ``` ``` /** * 存储值 * @param hash 重新计算的hash值 * @param key 键 * @param value 值 */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { // Node类型的数组;Node类型的节点;n,i Node<K,V>[] tab; Node<K,V> p; int n, i; // tab = Node[] = null if ((tab = table) == null || (n = tab.length) == 0) // n = (tab数组 = resize()扩容后返回的数组)的长度,默认为16 n = (tab = resize()).length; // (n - 1) & hash:数组的长度-1 & hash,确定存储的位置 // 判断数组的索引上是不是null,并赋值给p if ((p = tab[i = (n - 1) & hash]) == null) // 数组的索引 = 赋值新的节点对象 tab[i] = newNode(hash, key, value, null); else { // 数组的索引不为null,说明,要存储的对象已经有了 Node<K,V> e; K k; // 判断已经存在的对象和要存储对象的hash值、equals方法 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 { // 遍历该索引下的链表,和每个元素比较hashCode和equals,替换 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; } ``` ``` // 将存储的对象,再次计算哈希值 // 尽量降低哈希值的碰撞 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } ``` ``` // 数组的扩容 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; } 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 { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } 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) { 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 { // preserve order 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; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; } ```
上一篇:
Map 接口常用的方法
下一篇:
7.3 Map 的实现类:LinkedHashMap
该分类下的相关小册推荐:
Java性能调优实战
Mybatis合辑5-注解、扩展、SQL构建
经典设计模式Java版
Java必知必会-JDBC
Mybatis合辑2-Mybatis映射文件
Java面试指南
Java语言基础7-Java中的异常
Java语言基础14-枚举和注解
Java语言基础2-运算符
JAVA 函数式编程入门与实践
深入拆解 Java 虚拟机
Mybatis合辑3-Mybatis动态SQL