文章目录
- 1、Map 接口实现类-HashMap
- 1.1、HashMap 小结
- 1.2、HashMap 底层机制及源码剖析
- 1.3、HashMap触发扩容、树化
- 2、Map 接口实现类-Hashtable
- 2.1、HashTable 的基本介绍
- 2.2、Hashtable 和 HashMap 对比
- 3、Map 接口实现类-Properties
- 3.1、基本介绍
- 3.2、Properties 使用
1、Map 接口实现类-HashMap
1.1、HashMap 小结
1.2、HashMap 底层机制及源码剖析
package map_;
import java.util.HashMap;
@SuppressWarnings({"all"})
public class HashMapSource1 {
public static void main(String[] args) {
HashMap map = new HashMap();
map.put("java", 10); // ok
map.put("php", 10); // ok
map.put("java", 20); // 替换 value
System.out.println("map=" + map); // map={java=20, php=10}
}
}
/**
* 解读 HashMap 的源码
* 1. 执行构造器 new HashMap()
* 初始化加载因子 loadfactor = 0.75 // static final float DEFAULT_LOAD_FACTOR = 0.75f;
* HashMap$Node[] table = null
* public HashMap() {
* this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
* }
*
* 2. 执行 put 调用 hash 方法, 计算 key 的 hash 值 (h = key.hashCode()) ^ (h >>> 16)
* public V put(K key, V value) { // key = "java" value = 10
* return putVal(hash(key), key, value, false, true);
* }
*
* 3. 执行 putVal
* final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
* boolean evict) {
* Node<K,V>[] tab; Node<K,V> p; int n, i; // 辅助变量
* // 如果底层的 table 数组为 null, 或者 length =0 , 就扩容到 16
* if ((tab = table) == null || (n = tab.length) == 0)
* n = (tab = resize()).length;
* // 取出 hash 值对应的 table 的索引位置的 Node, 如果为 null, 就直接把加入的 k-v
* // 创建成一个 Node, 加入该位置即可
* if ((p = tab[i = (n - 1) & hash]) == null)
* tab[i] = newNode(hash, key, value, null);
* else {
* Node<K,V> e; K k; // 辅助变量
* // 如果 table 的索引位置的 key 的 hash 相同和新的 key 的 hash 值相同,
* // 并 满足(table 现有的结点的 key 和准备添加的 key 是同一个对象 || equals 返回真)
* // 就认为不能加入新的 k-v
* if (p.hash == hash &&
* ((k = p.key) == key || (key != null && key.equals(k))))
* e = p;
* else if (p instanceof TreeNode) // 如果当前的 table 的已有的 Node 是红黑树, 就按照红黑树的方式处理
* 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);
* // 加入后, 判断当前链表的个数, 是否已经到 8 个, 到 8 个后
* // 就调用 treeifyBin 方法进行红黑树的转换
* if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
* treeifyBin(tab, hash);
* break;
* }
* if (e.hash == hash && // 如果在循环比较过程中, 发现有相同, 就 break, 就只是替换 value
* ((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; // 替换, key 对应 value
* afterNodeAccess(e);
* return oldValue;
* }
* }
* ++modCount; // 每增加一个 Node, 就 size++
* if (++size > threshold) // 如 size > 临界值就扩容
* resize();
* afterNodeInsertion(evict);
* return null;
* }
*
* 5. 关于树化(转成红黑树)
* // 如果 table 为 null, 或者大小还没有到 64, 暂时不树化, 而是进行扩容
* // 否则才会真正的树化 -> 剪枝
* final void treeifyBin(Node<K,V>[] tab, int hash) {
* int n, index; Node<K,V> e;
* if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
* resize();
* else if ((e = tab[index = (n - 1) & hash]) != null) {
* TreeNode<K,V> hd = null, tl = null;
* do {
* TreeNode<K,V> p = replacementTreeNode(e, null);
* if (tl == null)
* hd = p;
* else {
* p.prev = tl;
* tl.next = p;
* }
* tl = p;
* } while ((e = e.next) != null);
* if ((tab[index] = hd) != null)
* hd.treeify(tab);
* }
* }
*/
1.3、HashMap触发扩容、树化
模拟HashMap触发扩容、树化情况,并 Debug验证
package map_;
import java.util.HashMap;
import java.util.Objects;
@SuppressWarnings({"all"})
public class HashMapSource2 {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
for (int i = 0; i < 12; i++) {
hashMap.put(i, "hello");
}
hashMap.put("aaa", "bbb");
System.out.println("hashMap=" + hashMap); // 12 个 k-v
// table 的扩容
// 0 -> 16(12) -> 32(24) -> 64(64*0.75=48)-> 128 (96)-> ...
}
}
class A {
private int num;
public A(int num) {
this.num = num;
}
// 所有的 A 对象的 hashCode 都是 100
@Override
public int hashCode() {
return 100;
}
}
2、Map 接口实现类-Hashtable
2.1、HashTable 的基本介绍
2.2、Hashtable 和 HashMap 对比
3、Map 接口实现类-Properties
3.1、基本介绍
3.2、Properties 使用
package map_;
import java.util.Properties;
public class Properties_ {
public static void main(String[] args) {
// 1. Properties 继承 Hashtable
// 2. 可以通过 k-v 存放数据, 当然 key 和 value 不能为 null
Properties properties = new Properties();
// properties.put(null, "abc"); // 抛出 空指针异常
// properties.put("abc", null); // 抛出 空指针异常
properties.put("john", 100); // k-v
properties.put("lucy", 100);
properties.put("lic", 100);
properties.put("lic", 88); // 如果有相同的 key, value 被替换
System.out.println("properties=" + properties); // properties={john=100, lic=88, lucy=100}
// 通过 k 获取对应值
System.out.println(properties.get("lic")); // 88
// 删除
properties.remove("lic");
System.out.println("properties=" + properties); // properties={john=100, lucy=100}
// 修改
properties.put("john", "约翰");
System.out.println("properties=" + properties); // properties={john=约翰, lucy=100}
}
}