有效地“修改”不可变映射
我们目前正在使用 Guava 来实现其不可变集合,但我惊讶地发现他们的地图没有方法可以通过较小的修改轻松创建新地图。最重要的是,他们的构建器不允许为键分配新值或删除键。
因此,如果我只想修改一个值,这就是我希望能够执行的操作:
ImmutableMap<Guid, ImmutableMap<String, Integer>> originalMap = /* get the map */;
ImmutableMap<Guid, ImmutableMap<String, Integer>> modifiedMap =
originalMap.cloneAndPut(key, value);
这就是 Guava 期望我执行的操作:
ImmutableMap<Guid, ImmutableMap<String, Integer>> originalMap = /* get the map */;
Map<Guid, ImmutableMap<String, Integer>> mutableCopy = new LinkedHashMap<>(originalMap);
mutableCopy.put(key, value);
originalMap = ImmutableMap.copyOf(mutableCopy);
/* put the map back */
通过执行此操作,我可以获得包含我想要的修改的地图的新副本。原始副本保持不变,我将使用原子引用将其放回原处,以便整个设置是线程安全的。
只是很慢。
这里有很多浪费的复制行为。假设地图中有 1,024 个桶。当您可以按原样使用这些不可变存储桶并仅克隆其中一个存储桶时,您无需重新创建 1,023 个存储桶(每个存储桶也创建两次)。
所以我猜:
是否有一个 Guava 实用方法埋藏在某处用于此类事情? (它不在 Maps 或 ImmutableMap.Builder 中。)
还有其他 Java 库可以正确处理此类事情吗?我的印象是 Clojure 背后有这种东西,但我们还没有准备好切换语言...
We're currently using Guava for its immutable collections but I was surprised to find that their maps don't have methods to easily create new maps with minor modifications. And on top of that, their builder doesn't allow assigning new values to keys, or removing keys.
So if I wanted to modify just one value, here's what I would like to be able to do:
ImmutableMap<Guid, ImmutableMap<String, Integer>> originalMap = /* get the map */;
ImmutableMap<Guid, ImmutableMap<String, Integer>> modifiedMap =
originalMap.cloneAndPut(key, value);
Here's what it looks like Guava are expecting me to do:
ImmutableMap<Guid, ImmutableMap<String, Integer>> originalMap = /* get the map */;
Map<Guid, ImmutableMap<String, Integer>> mutableCopy = new LinkedHashMap<>(originalMap);
mutableCopy.put(key, value);
originalMap = ImmutableMap.copyOf(mutableCopy);
/* put the map back */
By doing this I get a new copy of the map with the modification I want. The original copy is untouched and I will be using an atomic reference to put the thing back so the whole setup is thread-safe.
It's just slow.
There is a lot of wasted copying going on under the covers here. Suppose there's 1,024 buckets in the map. That's 1,023 buckets which you're unnecessarily creating all over again (twice each, too), when you could have used those immutable buckets as-is and cloned only one of them.
So I guess:
Is there a Guava utility method buried somewhere for this sort of thing? (It isn't in Maps or on the ImmutableMap.Builder.)
Is there any other Java library which gets this sort of thing right? I am under the impression that Clojure has this sort of thing under the hood but we're not ready to switch languages just yet...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
有点出乎意料的是,Functional Java 地图 与 Guava 的地图一样是可变的。正如我所期望的那样,该列表是不可变的。
谷歌搜索“持久集合java”,出现:pcollections。有一个地图实现。
在实际使用任何其他实现之前,我会针对 Guava 对内存和性能特征进行基准测试。如果它仍然更好,我不会感到惊讶。
A bit unexpected the map of Functional Java is mutable like Guava's. The list is immutable though as I would expect.
Googling for "persistent collection java" brought up: pcollections. There are's a Map implementation.
Before actually using any other implementation I would benchmark the memory and performance characteristics against Guava. I would not be surprised if it's still better.
人们可以采用以下方法,仅复制地图一次而不是两次:
但是,通过对现有地图进行迭代来删除值看起来不太美观,例如:
One may go with the following
, to just duplicate the map once instead of twice:However, removing a value looks quite less beautiful with an iteration over the existing map, e.g. like this: