我正在阅读别人使用
computeIfAbsent
的代码。我不经常使用 lambda,并且在尝试了解它的用途时要保持谨慎。根据我自己的理解重写这个,这是正确的解释吗?
list.computeIfAbsent(info.getOwner(), k -> new CopyOnWriteArrayList<>()).add(info);
Object owner = info.getOwner();
if (list.get(owner) == null)
list.put(owner, new CopyOnWriteArrayList<Info>(Arrays.asList(info)));
在这种情况下,我是否错过了不使用 lambda 方法的任何好处?文档中提到
computeIfAbsent()
是原子的,但是 add()
在 computeIfAbsent()
返回之后才会被调用。如果插入是原子的,putIfAbsent()
不是更合适吗?
不,它们不一样。
第一个案例
ArrayList
并将其添加到地图中。然后该列表将被返回并添加信息。第二种情况
ArrayList
。然后将添加新的 ArrayList 并将值添加到其中。map.get(owner)
不会为 null 并且 map.put 将不会执行。是的,您错过了福利。
putIfAbsent
?使用
putIfAbsent
,如果该东西不不存在,代码仍然会运行。您正在 still 创建一个新的 1 条目数组,其中包含 info
,still 制作一个包装该列表的包装器列表,still 制作一个 CopyOnWriteArrayList
复制该包装列表,并传递对此的引用COWList 到 putIfAbsent
其中...
立即将其扔进垃圾桶。
if
的事情呢?因为它不是原子的。当然,并非所有列表都是原子的,但第二个片段不可能是原子的。即使那个 list
引用(显然是一个 map
,这对于地图来说是一个非常糟糕的名字!)是同步的,该代码也不起作用:另一个线程可能会在你的
.get
之间偷偷地输入一个值
电话和您的 put
电话。那么这些 lambda 东西是什么?第二个片段有点好,它涵盖了您想要做的事情,即:检查 owner
,那么我想创建一个新的 COWList 并将其作为值与
owner
相关联。但如果是的话,什么也不做。
但是,它并不是自动完成的。如果你自己不控制原子性过程,你就无法解决这个问题,例如与 synchronized
。这是[A]限制,因为任何尝试编写复杂的多核安全方法都不会起作用,因为每次尝试与地图交互时都需要重复(例如,像ConcurrentMap
这样的东西是比这个东西复杂得多,而且 [B] 甚至还有相当多的代码。这个:
CopyOnWriteArrayList<Info> list;
synchronized {
list = map.get(owner);
if (list == null) {
map.put(owner, list = new CopyOnWriteArrayList<Info>(Arrays.asList(info)));
}
}
有很多东西要写。你每次都必须复制它。
你真正想做的是将一些东西交给map impl,以便它可以完成所有这些事情。但是,您需要给它一个配方:如果映射本身
确定键当前未与某事物关联,因此是时候制作该事物并将其设置为与键关联的值,则您需要将其交给它制作该东西的食谱。
这就是 lambda。食谱。 就像你可以吃蛋糕,但你不能吃蛋糕食谱一样,lambda 与其制作的东西不同。
就像您可以将菜谱烹饪 0 次、1 次或 100 次一样,您可以根据需要多次调用 lambda。其中包括“从不”——如果密钥不存在,
computeIfAbsent
代码将会执行此操作。
就像菜谱一样,很容易复制(您可以将 lambda 传递给周围,它们的“制作”成本很便宜,就像复制菜谱比复制蛋糕要简单得多)。
就像菜谱一样,它可以有参数。食谱通常只是说“拿起你的平底锅并加热”。它通常不包括如何一起制作烹饪锅的说明。相反,该食谱需要平底锅作为输入。没有平底锅吗?无法“调用”食谱。