关于Java的Map

总结

Java的Collections Framework提供了许多方便的Map实现。

在这里,我将简要介绍它们。

Collections Framework 中的Map 的完整概述

以下是Collections Framework提供的Map接口、抽象类和具体类的完整内容。

undefined

以下是接口的五个选项。

インターフェース名どんな機能?Map1対1に紐づくkey,valueを持つSortedMapkeyの順番でソートしているMapNavigableMapkeyによる検索結果で最も近いものを返せるMapConcurrentMapputIfAbsent, remove, replace について並行処理を安全にできるMapConcurrentNavigableMapConcurrentMapにNavigableMapの機能がついたMap

在抽象类中有AbstractMap,它扮演着类似于模板方法的角色。

如果自行分类具象类,可能是这样的。

    • keyにhashCodeを使う系

HashMap
LinkedHashMap

順序がある系

TreeMap

並行処理系

ConcurrentHashMap
ConcurrentSkipListMap

特殊系

EnumMap
WeakHashMap
IdentityHashMap

使用hashCode来作为键的一类

如果将hashCode用作Map的键,则只要适当实现与键对应的类的hashCode,就可以在O(1)的时间内进行数据搜索。

哈希映射

个人认为这是我经常遇到的最佳Map实现。
看起来可以在key和value中都放入null。

jshell> HashMap<String, String> a = new HashMap<String, String>();
a ==> {}

jshell> a.put(null, null);
$2 ==> null

jshell> a.get(null);
$3 ==> null

jshell> a.size();

链式哈希映射

这里的key和value都可以设置为null。

这个Map可以记住被添加的顺序。
但是,不能覆盖已经在Map中注册的元素的顺序。

jshell> LinkedHashMap<String, String> b = new LinkedHashMap<String, String>();
b ==> {}

jshell> b.put("1", "one");
$6 ==> null

jshell> b.put("2", "two");
$7 ==> null

jshell> for (Map.Entry<String, String> entry: b.entrySet()){
   ...>     System.out.println(entry.getKey());
   ...>     System.out.println(entry.getValue());
   ...> }
1
one
2
two

jshell> b.put("1", "one");
$9 ==> "one"

jshell> for (Map.Entry<String, String> entry: b.entrySet()){
   ...>     System.out.println(entry.getKey());
   ...>     System.out.println(entry.getValue());
   ...> }
1
one
2
two

有一個有次序的系統。

树图

这个Map的containsKey、get、put和remove操作的时间复杂度为O(log(n))。

在指定的键值以下,能够轻松地返回最接近该键的条目。这对竞技编程很有帮助。

jshell> c.put(1, "one");
$19 ==> null

jshell> c.put(100, "one hundred");
$20 ==> null

jshell> c.put(200, "two hundred");
$21 ==> null

jshell> c.floorEntry(150).getValue();
$22 ==> "one hundred"

TreeMap无法将null作为键。(可以将null放入值中。)

jshell> c.put(null, "one");
|  例外java.lang.NullPointerException
|        at Objects.requireNonNull (Objects.java:208)
|        at TreeMap.put (TreeMap.java:809)
|        at TreeMap.put (TreeMap.java:534)
|        at (#23:1)

jshell> c.put(300, null);
$24 ==> null

并发处理系统

在使用computeIfAbsent和computeIfPresent时,可以执行具有原子性的操作。

并发哈希表

在具有多个线程的情况下,在同时使用computeIfPresent更改value状态的操作中,执行具有原子性的处理。

import java.util.concurrent.ConcurrentHashMap;

public class Main {
    //    public static HashMap<String, Integer> map = new HashMap<>();
    public static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

    static {
        map.put("key", 1);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new MyTask());
        thread1.start();
        Thread thread2 = new Thread(new MyTask());
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println(map.get("key")); //ConcurrentHashMapを利用したら常に 201。HashMap だと不定。

    }

}

class MyTask implements Runnable {
    public void run() {
        for (int i = 0; i < 100; i++) {
            Main.map.computeIfPresent("key", (key, value) -> value + 1);
        }
    }
}

看起来key和value都不接受null。

jshell> d.put("a", null);
|  例外java.lang.NullPointerException
|        at ConcurrentHashMap.putVal (ConcurrentHashMap.java:1011)
|        at ConcurrentHashMap.put (ConcurrentHashMap.java:1006)
|        at (#27:1)

jshell> d.put(null,"a");
|  例外java.lang.NullPointerException
|        at ConcurrentHashMap.putVal (ConcurrentHashMap.java:1011)
|        at ConcurrentHashMap.put (ConcurrentHashMap.java:1006)
|        at (#28:1)

跳表图

好像是用跳跃表算法实现的。

即使有多个线程同时访问Map,也可以安全地进行注册、更新和删除操作。

似乎在key和value中都不接受null值。

特殊的系别

枚举映射

key只能是Enum类型的Map。

无法将key设置为null,但可以将value设置为null。

jshell> public enum DayOfWeek {
   ...>     MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
   ...> }
|  次を作成しました: 列挙型 DayOfWeek

jshell> EnumMap<DayOfWeek, String> e = new EnumMap<>(DayOfWeek.class);
e ==> {}

jshell> e.put(null, null);
|  例外java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "key" is null
|        at EnumMap.typeCheck (EnumMap.java:736)
|        at EnumMap.put (EnumMap.java:264)
|        at (#33:1)

jshell> e.put(DayOfWeek.MONDAY, null);
$34 ==> null

弱引用哈希表

这是一个具有这样的性质的Map:当与key相关联的对象不再被其他方式引用时,会触发垃圾回收并从Map中删除。我认为这个页面的解释很容易理解。

在哪里使用?当我调查Spring时,发现它在一个名为AbstractClassGenerator的类中被使用。

    private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>();

根据变量名推测,这应该是某种缓存。我决定不再深入追究(因为无法做到)。

WeakHashMap可以在key和value中都放入null。

jshell> WeakHashMap<Integer, String> g = new WeakHashMap<>();
g ==> {}

jshell> g.put(null, "a");
$2 ==> null

jshell> g.put(1, null);
$3 ==> null

jshell> g.size();
$4 ==> 2

身份HashMap

判定 key 是否相匹配的方法在 IdentityHashMap 中与其他 Map 不同。在其他 Map 中,判断两个 key 是否相等是通过 k1.equals(k2) 来判断的,而在 IdentityHashMap 中是通过 k1 == k2 来判断的。

jshell> HashMap<Integer, String> hash = new HashMap<>();
hash ==> {}

jshell> IdentityHashMap<Integer, String> identity = new IdentityHashMap<>();
identity ==> {}

jshell> hash.put(1, "one");
$29 ==> null

jshell> hash.get(new Integer(1));
|  警告:
|  java.lang.IntegerのInteger(int)は推奨されておらず、削除用にマークされています
|  hash.get(new Integer(1));
|           ^------------^
$30 ==> "one"

jshell> identity.put(1, "one");
$31 ==> null

jshell> identity.get(new Integer(1));
|  警告:
|  java.lang.IntegerのInteger(int)は推奨されておらず、削除用にマークされています
|  identity.get(new Integer(1));
|               ^------------^
$32 ==> null

IdentityHashMap可以在key和value中都存放null值。

结束时

我大致浏览了Collections Framework中的Map。挖掘每个Map在具体场景中的使用方式可能会很有趣。

广告
将在 10 秒后关闭
bannerAds