Java unmodifiableMap 可以用'Map.copyOf'调用替换

Sai*_*ibō 5 java

我是 Java 新手,最近我了解到有时深度复制 Collection 并对其进行不可修改的视图很重要,这样里面的数据才能保持安全和不变。当我尝试练习这个(unmodifiableMap2)时,我收到了来自IDEA的警告

unmodifiableMap 可以替换为 'Map.copyOf' 调用

这对我来说是连线的,因为我认为 unmodifiableMap 不仅仅是底层地图的副本。此外,当我尝试以另一种方式(unmodifiableMap1)创建相同的 unmodifiableMap 时,不会弹出警告!

我应该如何理解 IDEA 的这种行为?

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class test {
    public static void main(String[] args) {
        Map<Integer, Integer> map = new HashMap<>();
        map.put(1,1);
        map.put(2,2);

        Map<Integer, Integer> map1 = new HashMap<>(map);

        Map<Integer, Integer> unmodifiableMap1 = Collections.unmodifiableMap(map1);

        Map<Integer, Integer> unmodifiableMap2 = Collections.unmodifiableMap(new HashMap<>(map););

    }
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

tqu*_*rat 7

Map.copyOf()制作给定实例的副本Map,但它要求映射中没有值为null。通常情况是这样,但这并不是Map一般情况下的严格要求。

\n

java.util.Collections.unmodifiableMap()只是包装对给定实例的引用Map。这意味着接收者无法修改映射,但对原始映射(即 的参数unmodifiableMap())的修改对接收者是可见的。

\n

假设我们有两个线程,一个线程迭代不可修改的映射,而另一个线程修改原始映射。结果,您可能会ConcurrentModificationException在不可修改的映射 \xe2\x80\xa6 上得到一个 for 操作,调试那个东西并不有趣!

\n

对于由 所创建的副本,这种情况不会发生Map.copyOf()。但这是有代价的:使用副本,您需要两倍的地图内存量(大约)。对于非常大的地图,这可能会导致内存短缺高达OutOfMemoryError. 调试也不好玩!

\n

此外,仅包装现有地图可能比复制它要快得多

\n

所以一般来说没有最好的解决方案,但对于大多数场景,我更倾向于Map.copyOf()在需要不可修改的地图时使用。

\n
\n

问题中的示例没有包装原始 Map实例,但它在包装之前制作了一个副本(无论是在其自己的行中,还是动态地)。这消除了“幕后”修改的潜在问题,但可能会带来内存问题。

\n

从我迄今为止的经验来看,Map.copyOf( map )看起来比Collections.unmodifiableMap( new HashMap( map ) ).

\n
\n

顺便说一下:Map.copyOf()返回一个类似于HashMap;的地图。当您用它复制 a 时TreeMap,排序顺序会丢失,而包装 withunmodifiableMap()会保留底层Map实现,因此也会保留排序顺序。因此,当这很重要时,您可以使用Collections.unmodifiableMap( new TreeMap( map ) ), whileMap.copyOf()在这里不起作用。

\n


Mic*_*ael 3

使用现有地图引用的不可修改地图完全没问题,您可能想要这样做的原因有很多。

考虑这个类:

class Foo {
    private final Map<String, String> fooMap = new HashMap<>();

    // some methods which mutate the map

    public Map<String, String> getMap() {
        return Collections.unmodifiableMap(fooMap);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个类的作用是提供它封装的映射的只读视图。该类可以确保使用地图的客户端无法更改它,他们只能看到其内容。如果他们保留参考一段时间,他们还可以看到条目的任何更新。

如果我们尝试通过复制映射来公开只读视图,则执行复制将花费时间和内存,并且客户端不会看到任何更改,因为两个映射都是不同的实例 - 源和副本。

但是在这种情况下:

Collections.unmodifiableMap(new HashMap<>(map));
Run Code Online (Sandbox Code Playgroud)

您首先将映射复制到新的哈希映射中,然后将该副本传递到Collections.unmodifiableMap. 结果实际上是恒定的。您没有对使用 创建的副本的引用new HashMap<>(map),也无法获得*。

如果您想要的是常量映射,那么Map.copyOf这是一种更简洁的实现方式,因此 IntelliJ 建议您应该使用它。

在第一种情况下,由于对地图的引用已经存在,IntelliJ 无法对您的意图做出相同的推断,因此它不会给出这样的建议。

如果您愿意,您可以查看此功能的 IntelliJ 票证,尽管它没有解释为什么两者本质上是等效的,只是它们确实是等效的。


* 好吧,你可能可以通过反射,但 IntelliJ 假设你不会