Java 从 Map 到 HashMap 的一步步实现

Java 从 Map 到 HashMap 的一步步实现一、 Map1.1 Map 接口在 Java 中, Map 提供了键——值的映射关系。映射不能包含重复的键,并且每个键只能映射到一个值。以 Map 键——值映射为基础,java.util 提供了 HashMap(最常用)、 TreeMap、Hashtble、LinkedHashMap 等数据结构。衍生的几种 Map 的主要特点:HashMap:最常用的数据结构。键和值之间通过 Hash函数 来实现映射关系。当进行遍历的 key 是无序的
摘要由CSDN通过智能技术生成

Java 从 Map 到 HashMap 的一步步实现

一、 Map

1.1 Map 接口

在 Java 中, Map 提供了键——值的映射关系。映射不能包含重复的键,并且每个键只能映射到一个值。
以 Map 键——值映射为基础,java.util 提供了 HashMap(最常用)、 TreeMap、Hashtble、LinkedHashMap 等数据结构。
衍生的几种 Map 的主要特点:

  • HashMap:最常用的数据结构。键和值之间通过 Hash函数 来实现映射关系。当进行遍历的 key 是无序的
  • TreeMap:使用红黑树构建的数据结构,因为红黑树的原理,可以很自然的对 key 进行排序,所以 TreeMap 的 key 遍历时是默认按照自然顺序(升序)排列的。
  • LinkedHashMap: 保存了插入的顺序。遍历得到的记录是按照插入顺序的。

1.2 Hash 散列函数

Hash (散列函数)是把任意长度的输入通过散列算法变换成固定长度的输出。Hash 函数的返回值也称为 哈希值 哈希码 摘要或哈希。Hash作用如下图所示:

引用自维基百科

Hash 函数可以通过选取适当的函数,可以在时间和空间上取得较好平衡。
解决 Hash 的两种方式:拉链法和线性探测法

1.3 键值关系的实现

 interface Entry<K,V> 

在 HashMap 中基于链表的实现

  static class Node<K,V> implements Map.Entry<K,V> {
   
        final int hash;
        final K key;
        V value;
        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;
        }

用树的方式实现:

  static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
   
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
   
            super(hash, key, val, next);
        }

1.4 Map 约定的 API

1.4.1 Map 中约定的基础 API

基础的增删改查:

int size();  // 返回大小
boolean isEmpty(); // 是否为空
boolean containsKey(Object key); // 是否包含某个键
boolean containsValue(Object value); // 是否包含某个值
V get(Object key); // 获取某个键对应的值 
V put(K key, V value); // 存入的数据 
V remove(Object key); // 移除某个键
void putAll(Map<? extends K, ? extends V> m); //将将另一个集插入该集合中
void clear();  // 清除
Set<K> keySet(); //获取 Map的所有的键返回为 Set集合
Collection<V> values(); //将所有的值返回为 Collection 集合
Set<Map.Entry<K, V>> entrySet(); // 将键值对映射为 Map.Entry,内部类 Entry 实现了映射关系的实现。并且返回所有键值映射为 Set 集合。 
boolean equals(Object o); 
int hashCode(); // 返回 Hash 值
default boolean replace(K key, V oldValue, V newValue); // 替代操作
default V replace(K key, V value);

1.4.2 Map 约定的较为高级的 API

default V getOrDefault(Object key, V defaultValue); //当获取失败时,用 defaultValue 替代。

default void forEach(BiConsumer<? super K, ? super V> action)  // 可用 lambda 表达式进行更快捷的遍历

default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function); 

default V putIfAbsent(K key, V value);

default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction);

default V computeIfPresent(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction);

default V compute(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction)

default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction)           
1.4.3 Map 高级 API 的使用
  • getOrDefault() 当这个通过 key获取值,对应的 key 或者值不存在时返回默认值,避免在使用过程中 null 出现,避免程序异常。
  • ForEach() 传入 BiConsumer 函数式接口,表达的含义其实和 Consumer 一样,都 accept 拥有方法,只是 BiConsumer 多了一个 andThen() 方法,接收一个BiConsumer接口,先执行本接口的,再执行传入的参数的 accept 方法。
      Map<String, String> map = new HashMap<>();
        map.put("a", "1");
        map.put("b", "2");
        map.put("c", "3");
        map.put("d", "4");
        map.forEach((k, v) -> {
   
            System.out.println(k+"-"+v);
        });
    }

更多的函数用法:
菜鸟教程-Java HashMap

1.5 从 Map 走向 HashMap

HashMap 是 Map的一个实现类,也是 Map 最常用的实现类。

1.5.1 HashMap 的继承关系
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable 

在 HashMap 的实现过程中,解决 Hash冲突的方法是拉链法。因此从原理来说 HashMap 的实现就是 数组 + 链表(数组保存链表的入口)。 当链表过长,为了优化查询速率,HashMap 将链表转化为红黑树(数组保存树的根节点),使得查询速率为 log(n),而不是链表的 O(n)。

二、HashMap

/*
 * @author  Doug Lea
 * @author  Josh Bloch
 * @author  Arthur van Hoff
 * @author  Neal Gafter
 * @see     Object#hashCode()
 * @see     Collection
 * @see     Map
 * @see     TreeMap
 * @see     Hashtable
 * @since   1.2
 */

首先 HashMap 由 Doug Lea 和 Josh Bloch 两位大师的参与。同时 Java 的 Collections 集合体系,并发框架 Doug Lea 也做出了不少贡献。

2.1 基本原理

对于一个插入操作,首先将键通过 Hash 函数转化为数组的下标。 若该数组为空,直接创建节点放入数组中。若该数组下标存在节点,即 Hash 冲突,使用拉链法,生成一个链表插入。

引用图片来自 Java集合之一—HashMap

如果存在 Hash 冲突,使用拉链法插入,我们可以在这个链表的头部插入,也可以在链表的尾部插入,所以在 JDK 1.7 中使用了头部插入的方法,JDK 1.8 后续的版本中使用尾插法。JDK 1.7 使用头部插入的可能依据是最近插入的数据是最常用的,但是头插法带来的问题之一,在多线程会链表的复制会出现死循环。所以 JDK 1.8 之后采用的尾部插入的方法。
在 HashMap 中,前面说到的 数组+链表 的数组的定义

transient Node<K,V>[] table;

链表的定义:

 static class Node<K,V> implements Map.Entry
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值