我是 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)
Map.copyOf()制作给定实例的副本Map,但它要求映射中没有值为null。通常情况是这样,但这并不是Map一般情况下的严格要求。
java.util.Collections.unmodifiableMap()只是包装对给定实例的引用Map。这意味着接收者无法修改映射,但对原始映射(即 的参数unmodifiableMap())的修改对接收者是可见的。
假设我们有两个线程,一个线程迭代不可修改的映射,而另一个线程修改原始映射。结果,您可能会ConcurrentModificationException在不可修改的映射 \xe2\x80\xa6 上得到一个 for 操作,调试那个东西并不有趣!
对于由 所创建的副本,这种情况不会发生Map.copyOf()。但这是有代价的:使用副本,您需要两倍的地图内存量(大约)。对于非常大的地图,这可能会导致内存短缺高达OutOfMemoryError. 调试也不好玩!
此外,仅包装现有地图可能比复制它要快得多。
\n所以一般来说没有最好的解决方案,但对于大多数场景,我更倾向于Map.copyOf()在需要不可修改的地图时使用。
问题中的示例没有包装原始 Map实例,但它在包装之前制作了一个副本(无论是在其自己的行中,还是动态地)。这消除了“幕后”修改的潜在问题,但可能会带来内存问题。
从我迄今为止的经验来看,Map.copyOf( map )看起来比Collections.unmodifiableMap( new HashMap( map ) ).
顺便说一下:Map.copyOf()返回一个类似于HashMap;的地图。当您用它复制 a 时TreeMap,排序顺序会丢失,而包装 withunmodifiableMap()会保留底层Map实现,因此也会保留排序顺序。因此,当这很重要时,您可以使用Collections.unmodifiableMap( new TreeMap( map ) ), whileMap.copyOf()在这里不起作用。
使用现有地图引用的不可修改地图完全没问题,您可能想要这样做的原因有很多。
考虑这个类:
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 假设你不会
| 归档时间: |
|
| 查看次数: |
259 次 |
| 最近记录: |