Java面向对象之Map 接口实现类-HashMap、Map 接口实现类-Hashtable 及 Map 接口实现类-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}
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值