ConcurrentMap.compute用作remove

时间:2015-07-02 09:35:46

标签: java collections java-8 java.util.concurrent concurrenthashmap

使用Java 8,我在java.util.concurrent.ConcurrentMap接口上实现了一个包装器,特别是remove(Object key)方法。由于我需要在删除期间进行更多检查,因此我需要使用

实现功能

compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

但是,问题在于:我必须接受Object,但将其作为compute传递给K。使用泛型,我甚至无法像if (key instanceof K) ...

那样进行运行时检查

我理解为什么compute的签名是这样选择的;如果它只是Object,如果计算需要创建一个新条目,它就不能简单地使用密钥。但我不知道如何解决这个问题 - 有没有推荐的模式,但是如下所示使用多个调用?

for (;;) {
    V v = map.get();
    if (check(k, v)) {
        if (map.remove(k, v)) return true;
    } else return false;
}

由于

3 个答案:

答案 0 :(得分:1)

查看ConcurrentMap.compute的默认实现,似乎您可以安全地使用未经检查的强制转换,假设您的remappingFunction可以处理Object而不是K类型并返回{ {1}}。所以你可以使用:

null

public V remove(Object key) { @SuppressWarnings("unchecked") V result = compute((K)key, (k, v) -> { if(v == null) return null; ... }); ... } 实现首先使用compute(接受任何对象,因此它是安全的),并将结果传递给get(key)。如果密钥的类型无效,则结果为remappingFunction,因此您的null也应返回remappingFunction。在这种情况下,null将被调用,它也会接受任何对象,并会返回containsKey(key)作为无效对象,而false将返回compute

请注意ConcurretMap.compute的行为已有详细记录(即使提供了等效的代码),因此将来不太可能破坏此类实现。

答案 1 :(得分:1)

我想到的第一个想法是使用containsKey预先检查地图是否可以处理特定的关键对象。如果此方法返回true,则地图可以处理它,因此未经检查的强制转换为K即可。

但是...

ConcurrentMap<String,Integer> map=new ConcurrentSkipListMap<>();
map.put("foo", 42);
map.containsKey(0);

已足以证明,尽管方法containsKey具有Object作为参数类型,但它不一定适用于任意参数类型 - 在这种情况下它会抛出{{1} }。

也就是说,使用ClassCastExceptionget(Object)的原始循环也无法保证正常工作。

如果没有其他类型的令牌,只有一种类型安全的方法:

remove(Object,Object)

但除了线性搜索的性能之外,如果在遍历期间同时插入相同的密钥,则理论上可能会多次删除密钥。

因此,原子和类型安全的唯一解决方案是求助于运行时类型令牌(即通常通过类文字初始化):

return map.entrySet().removeIf(e ->
    Objects.equals(key, e.getKey()) && check(e.getKey(), e.getValue()));

请注意,您可以使用

之类的内容
ConcurrentMap<K, V> map;
Class<K> keyType;

public boolean remove(Object key) {
    if(!keyType.isInstance(key)) return false;
    K k=keyType.cast(key);
    for(;;) {
        V v=map.get(k);
        if(v==null || !check(k,v)) return false;
        if(map.remove(k, v)) return true;
    }
}

以原子方式删除映射(如果存在且if(keyType.isInstance(key)) map.computeIfPresent(keyType.cast(key), (k,v) -> check(k, v)? null: v); 成功,但您无法使用它来返回是否实际发生了删除,因为check方法之间没有区别在两种情况下都会删除礼物和钥匙,compute…将被退回。

答案 2 :(得分:0)

看看你是否可以使用Map的forEach方法并将其放入forEach

map.forEach((k, v) -> 
System.out.println(k + "=" + v));