Map接口源码分析

package java.util;

import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.io.Serializable;

/**
 * 1.概述
 *   (1) 实现键值对映射的对象。Map存放的键值对(key-value)中,不能包含重复的键(键可以为null);每个键最多只能映射到一个值。
 *   (2) Map接口替换了Dictionary虚类。映射的顺序定义为映射集合视图上的迭代器返回其元素的顺序。
 *   (3) 尝试查询是否存在不合格的键或值可能会引发异常,而该操作的完成不会导致将不合格元素插入映射中,则可能会引发异常,
 *       也可能会成功(由实现选择),此类异常在该接口的规范中标记为“可选”。
 * 2.构造方法: 所有通用的映射实现类都应该提供两个“标准”构造函数:
 *   (1) 一个是void(无参数)构造函数,它创建一个空映射;
 *   (2) 另一个是具有一个类型为map的参数的构造函数,它创建一个新的映射,其键值映射与其参数相同。
 *      此构造函数允许用户复制任何映射,从而生成所需类的等效映射。
 *      没有办法强制执行这个建议(因为接口不能包含构造函数),但是JDK中的所有通用映射实现都符合。
 * 3.实现类
 *   HashMap:不保证插入元素的顺序
 *   TreeMap:插入时可以依旧key进行排序
 * 4.异常
 *   UnsupportedOperationException:该操作不被支持时抛出此异常;
 *   NullPointerException:
 *   ClassCastException:插入的key的类型不符合当前map上定义的泛型;
 * 注意点:
 * (1)不允许Map自身作为一个key来使用;
 * (2)每个key有且只映射一个value,key唯一且可以为空;
 *
 * @param <K> 此Map维护的Key的类型
 * @param <V> 此Map维护的Value的类型
 *
 * @author  Josh Bloch
 * @see HashMap
 * @see TreeMap
 * @see Hashtable
 * @see SortedMap
 * @see Collection
 * @see Set
 * @since 1.2
 */
public interface Map<K,V> {
    // 查询操作
    int size();         //返回键值对映射的个数。如果键值对个数超过Integer.MAX_VALUE,则返回Integer.MAX_VALUE。
    boolean isEmpty();  //如果此映射不包含任何键值映射,则返回true;否则返回false。

    /**
     * @param  key 需要测试的键
     * @return 如果映射包含key,则返回true;否则返回false。更标准的判断条件是:(key==null ? k==null : key.equals(k))
     * @throws ClassCastException 如果key的类型不符合map的类型
     * @throws NullPointerException 如果指定的键为null并且此映射不允许空键
     */
    boolean containsKey(Object key);

    /**
     * @param  value 需要测试的值
     * @return 如果映射包含一个或多个value,则返回true;否则返回false。更标准的判断条件是:(value==null ? v==null : value.equals(v))
     * @throws ClassCastException 如果key的类型不符合map的类型
     *         NullPointerException 如果指定的键为null并且此映射不允许空键
     */
    boolean containsValue(Object value);

    /**
     * @param  key 映射的key
     * @return 返回映射中key对应的value值。如果value不存在则返回空。另外,如果key对应的value为空时,也会返回空。
     * @throws ClassCastException 如果key的类型不符合map的类型
     * @throws NullPointerException 如果指定的键为null并且此映射不允许空键
     */
    V get(Object key);

    // 修改操作

    /**
     * 存放一对key-value映射,如果key已存在(当且仅当map.containsKey(key)==true),则覆盖旧的value
     * @param key   映射的key
     * @param value 映射的value
     * @return 如果该key已在map中存在,则返回以前与此key关联的值,否则返回null。如果以前存储的value是空,也可能会返回空
     * @throws UnsupportedOperationException 如果map不支持put操作
     * @throws ClassCastException 如果key或value的类型不符合map的类型
     * @throws NullPointerException 如果指定的键或值为null并且此映射不允许空键和空值
     * @throws IllegalArgumentException 如果指定键或值的某些属性不允许它存储在此映射中
     */
    V put(K key, V value);

    /**
     * 根据key移除map中的此key的映射
     * @param key 映射的key
     * @return 返回以前与此key关联的value,如果map中不存在此key或者此key以前对应的value是空,则返回空
     * @throws UnsupportedOperationException 如果此映射不支持 remove 操作
     * @throws ClassCastException 如果该键对于此映射是不合适的类型(可选)
     * @throws NullPointerException 如果指定键为 null 并且此映射不允许 null 键(可选)
     */
    V remove(Object key);


    // 批量数据操作
    /**
     * 从指定映射中将所有映射关系复制到此映射中(可选操作)。
     * 对于指定映射中的每个键k到值v的映射关系,此调用等效于对此映射调用一次put(k, v)。
     * 如果正在进行此操作的同时修改了指定的映射,则此操作的行为是不确定的。
     * @param m 要存储在此映射中的映射关系
     * @throws UnsupportedOperationException 如果此映射不支持 putAll 操作
     * @throws ClassCastException 如果指定映射中的键或值的类不允许将其存储在此映射中
     * @throws NullPointerException 如果指定映射为null,或者此映射不允许null键或值,并且指定的映射包含null键或值
     * @throws IllegalArgumentException 如果指定映射中的键或值的某些属性不允许将其存储在此映射中
     */
    void putAll(Map<? extends K, ? extends V> m);

    void clear();   // 移除map中所有的键值对映射,这个map的大小将会变成0;@throws UnsupportedOperationException:如果此映射不支持清除操作


    // 视图(查询操作)
    /**
     * 返回此映射中包含的键的Set视图。
     * 该set受映射支持,所以对映射的更改可在此 set 中反映出来,反之亦然。
     * 如果对该set进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),则迭代结果是不确定的。
     * set支持元素移除,通过 Iterator.remove、 Set.remove、 removeAll、 retainAll 和 clear 操作可从映射中移除相应的映射关系。
     * 它不支持 add 或 addAll 操作。
     * @return 此映射中包含的所有键的set视图
     */
    Set<K> keySet();

    /**
     * 返回此映射中包含的值的 Collection 视图。
     * 该 collection 受映射支持,所以对映射的更改可在此collection 中反映出来,反之亦然。
     * 如果对该 collection 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),则迭代结果是不确定的。collection 支持元素移除,通过 Iterator.remove、 Collection.remove、 removeAll、 retainAll 和 clear 操作可从映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
     * @return 此映射中包含的值的 collection 视图
     */
    Collection<V> values();

    /**
     * 返回此映射中包含的映射关系的 Set 视图。
     * 该 set 受映射支持,所以对映射的更改可在此 set 中反映出来,反之亦然。
     * 如果对该 set 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作,或者通过对迭代器返回的映射项执行 setValue 操作除外),则迭代结果是不确定的。
     * set 支持元素移除,通过 Iterator.remove、 Set.remove、 removeAll、 retainAll 和 clear 操作可从映射中移除相应的映射关系。
     * 它不支持 add 或 addAll 操作。
     * @return 此映射中包含的映射关系的 set 视图
     */
    Set<Map.Entry<K, V>> entrySet();

    /**
     * 映射项(键-值对)。
     * Map.entrySet 方法返回映射的 collection 视图,其中的元素属于此类。
     * 获得映射项引用的唯一 方法是通过此 collection 视图的迭代器来实现。
     * 这些 Map.Entry 对象仅 在迭代期间有效;
     * 更确切地讲,如果在迭代器返回项之后修改了底层映射,则某些映射项的行为是不确定的,除了通过 setValue 在映射项上执行操作之外。
     * @see Map#entrySet()
     * @since 1.2
     */
    interface Entry<K,V> {
        /**
         * 返回与此项对应的键。
         * @return 与此项对应的键
         * @throws IllegalStateException 如果已经从底层映射中移除了该项,则实现可能(但不要求)抛出此异常。
         */
        K getKey();

        /**
         * 返回与此项对应的值。如果已经从底层映射中移除了映射关系(通过迭代器的 remove 操作),则此调用的结果是不确定的。
         * @return 与此项对应的值
         * @throws IllegalStateException 如果已经从底层映射中移除了该项,则实现可能,但不要求,抛出此异常。
         */
        V getValue();

        /**
         * 用指定的值替换与此项对应的值(可选操作)写入该映射。
         * 如果已经从映射中移除了映射关系(通过迭代器的 remove 操作),则此调用的行为是不确定的。
         * @param value 要存储在此项中的新值
         * @return 与此项对应的旧值
         * @throws UnsupportedOperationException 如果底层映射不支持 put 操作
         * @throws ClassCastException 如果指定值的类不允许将该值存储在底层映射中
         * @throws NullPointerException 如果底层映射不允许 null 值,并且指定的值为 null
         * @throws IllegalArgumentException 如果此值的某些方面不允许将其存储在底层映射中
         * @throws IllegalStateException 如果已经从底层映射中移除了该项,则实现可能,但不要求,抛出此异常.
         */
        V setValue(V value);

        /**
         * 比较指定对象与此项的相等性。如果给定对象也是一个映射项,并且两个项表示相同的映射关系,则返回 true。
         * 更确切地讲,如果满足以下条件,则两个项 e1 和 e2 才表示相同的映射关系
         *     (e1.getKey()==null ? e2.getKey()==null : e1.getKey().equals(e2.getKey()))  &&
         *       (e1.getValue()==null ? e2.getValue()==null : e1.getValue().equals(e2.getValue()))
         * 这可以确保 equals 方法在不同的 Map.Entry 接口实现间可正确地工作。
         * @param o  要与此映射项进行相等性比较的对象
         * @return 如果指定的对象等于此映射项,则返回 true
         */
        boolean equals(Object o);

        /**
         * 返回此映射项的哈希码值。映射项 e 的哈希码定义如下:
         *     (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
         *      (e.getValue()==null ? 0 : e.getValue().hashCode())
         * 这确保 e1.equals(e2) 意味着对于任意两个项 e1 和 e2 而言, e1.hashCode()==e2.hashCode(),这正是 Object.hashCode 的常规协定所要求的。
         * @return 映射项的哈希码值
         * @see Object#hashCode()
         * @see Object#equals(Object)
         * @see #equals(Object)
         */
        int hashCode();

        /**
         * Returns a comparator that compares {@link Map.Entry} in natural order on key.
         *
         * <p>The returned comparator is serializable and throws {@link
         * NullPointerException} when comparing an entry with a null key.
         *
         * @param  <K> the {@link Comparable} type of then map keys
         * @param  <V> the type of the map values
         * @return a comparator that compares {@link Map.Entry} in natural order on key.
         * @see Comparable
         * @since 1.8
         */
        public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                    (c1, c2) -> c1.getKey().compareTo(c2.getKey());
        }

        /**
         * Returns a comparator that compares {@link Map.Entry} in natural order on value.
         *
         * <p>The returned comparator is serializable and throws {@link
         * NullPointerException} when comparing an entry with null values.
         *
         * @param <K> the type of the map keys
         * @param <V> the {@link Comparable} type of the map values
         * @return a comparator that compares {@link Map.Entry} in natural order on value.
         * @see Comparable
         * @since 1.8
         */
        public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                    (c1, c2) -> c1.getValue().compareTo(c2.getValue());
        }

        /**
         * Returns a comparator that compares {@link Map.Entry} by key using the given
         * {@link Comparator}.
         *
         * <p>The returned comparator is serializable if the specified comparator
         * is also serializable.
         *
         * @param  <K> the type of the map keys
         * @param  <V> the type of the map values
         * @param  cmp the key {@link Comparator}
         * @return a comparator that compares {@link Map.Entry} by the key.
         * @since 1.8
         */
        public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                    (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
        }

        /**
         * Returns a comparator that compares {@link Map.Entry} by value using the given
         * {@link Comparator}.
         *
         * <p>The returned comparator is serializable if the specified comparator
         * is also serializable.
         *
         * @param  <K> the type of the map keys
         * @param  <V> the type of the map values
         * @param  cmp the value {@link Comparator}
         * @return a comparator that compares {@link Map.Entry} by the value.
         * @since 1.8
         */
        public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                    (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
        }
    }

    // 比较和哈希
    /**
     * 比较指定的对象与此映射是否相等。
     * 如果给定的对象也是一个映射,并且这两个映射表示相同的映射关系,则返回 true。
     * 更确切地讲,如果 m1.entrySet().equals(m2.entrySet()),则两个映射 m1 和 m2 表示相同的映射关系。
     * 这可以确保 equals 方法在不同的 Map 接口实现间运行正常。
     * @param o 要与此映射进行相等性比较的对象
     * @return 如果指定的对象等于此映射,则返回 true
     */
    boolean equals(Object o);

    /**
     * 返回此映射的哈希码值。
     * 映射的哈希码定义为此映射 entrySet() 视图中每个项的哈希码之和。
     * 这确保 m1.equals(m2) 对于任意两个映射 m1 和 m2 而言,都意味着 m1.hashCode()==m2.hashCode(),正如 Object.hashCode() 常规协定的要求。
     * @return 返回map的hashCode值
     * @see Map.Entry#hashCode()
     * @see Object#equals(Object)
     * @see #equals(Object)
     */
    int hashCode();

    // 默认方法,其具体用法可以参考这篇博客:https://geek-docs.com/java/java-examples/the-default-method-of-java-map-interface.html
    // 1.8新特性:如果key存在则返回value,否则返回传递的defaultValue
    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
                ? v
                : defaultValue;
    }

    // 1.8新特性:对 Map 进行迭代,将所有键和值传递给 Consumer
    default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }

    // 1.8新特性:将 Map 中每个条目的值替换为对当前条目调用给定函数后的结果
    default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }

            // ise thrown from function is not a cme.
            v = function.apply(k, v);

            try {
                entry.setValue(v);
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
        }
    }

    // 1.8新特性:如果value为空,则存放此映射;否则不存放
    default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }

    // 1.8新特性:如果此映射存在(key-value和映射中的相同)则移除此映射
    default boolean remove(Object key, Object value) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, value) ||
                (curValue == null && !containsKey(key))) {
            return false;
        }
        remove(key);
        return true;
    }

    // 1.8新特性:如果此映射存在(key-value和映射中的相同)则使用新value值替换此映射的旧的value
    default boolean replace(K key, V oldValue, V newValue) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, oldValue) ||
                (curValue == null && !containsKey(key))) {
            return false;
        }
        put(key, newValue);
        return true;
    }

    // 1.8新特性:如果key存在或者(value存在且不为空),则替换此映射value
    default V replace(K key, V value) {
        V curValue;
        if (((curValue = get(key)) != null) || containsKey(key)) {
            curValue = put(key, value);
        }
        return curValue;
    }

    // 1.8新特性:如果键存在,返回对应的值,否则通过提供的函数计算新的值并保存
    default V computeIfAbsent(K key,
                              Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }

    // 1.8新特性:计算新的值以替换现有的值
    default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue;
        if ((oldValue = get(key)) != null) {
            V newValue = remappingFunction.apply(key, oldValue);
            if (newValue != null) {
                put(key, newValue);
                return newValue;
            } else {
                remove(key);
                return null;
            }
        } else {
            return null;
        }
    }

    // 1.8新特性:根据现有的键和值计算新的值
    default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue = get(key);

        V newValue = remappingFunction.apply(key, oldValue);
        if (newValue == null) {
            // delete mapping
            if (oldValue != null || containsKey(key)) {
                // something to remove
                remove(key);
                return null;
            } else {
                // nothing to do. Leave things as they were.
                return null;
            }
        } else {
            // add or replace old mapping
            put(key, newValue);
            return newValue;
        }
    }

    // 1.8新特性:如果键在 Map 中不存在,返回提供的值,否则计算新的值
    default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                remappingFunction.apply(oldValue, value);
        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMap 是 Java 中非常重要的数据结构之一,它实现了 Map 接口,提供了快速的键值对的查找和存储功能。下面是 HashMap源码分析: 1. 数据结构 HashMap 内部实现了一个数组,每个数组元素是一个单向链表,称为桶(bucket)。当我们向 HashMap 中添加一对键值对时,会根据键的哈希值(hashcode)计算出该键值对应该存储在哪个桶中。如果该桶中已经有了该键值对,就将该键值对添加到桶的末尾(Java 8 中是添加到桶的头部),否则就创建一个新的节点添加到桶的末尾。 2. 哈希冲突 如果两个键的哈希值相同,就称为哈希冲突。HashMap 采用链表法解决哈希冲突,即将哈希值相同的键值对存储在同一个桶中,通过单向链表组织起来。当我们根据键查找值时,先根据键的哈希值找到对应的桶,然后遍历该桶中的链表,直到找到目标键值对或者链表为空。 3. 扩容机制 当 HashMap 中的键值对数量超过了桶的数量的时候,就需要对 HashMap 进行扩容。扩容会重新计算每个键值对的哈希值,并将它们存储到新的桶中。Java 8 中,HashMap 的扩容机制发生了一些变化,采用了红黑树等优化方式。 4. 线程安全 HashMap 是非线程安全的,如果多个线程同时操作同一个 HashMap,就有可能导致数据不一致的问题。如果需要在多线程环境下使用 HashMap,可以使用 ConcurrentHashMap。 以上就是 HashMap源码分析,希望对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值