我想知道如何做以下的事情
class MyCollection<E> implements Collection<E> {
@Nullable E findAndRemove(Predicate<E> predicate) {
for (E e : this) {
if (predicate.test(e)) {
remove(e);
return e;
}
}
return null;
}
}
以线程安全的方式。它实际上不必是Collection
,因为唯一需要的操作是add
和findAndRemove
。注意
removeIf
不相关,因为它删除所有匹配元素并且不提供任何回报CopyOnWriteArrayList
可能会这样做,但是Javadoc说“有效的时候...遍历操作的数量远远超过突变”,这绝对不是真的。CopyOnWriteArrayList
天真时很容易发生关于过早的优化和XY问题:是的,我知道!当我在一天中可能或不需要的东西鬼混时,我进入了这个,但我发现这个问题本身很有趣。
答案 0 :(得分:2)
由于您只需要add
和findAndRemove
方法,因此某些类型的并发哈希是自然的选择,因为自Java 1.5以来已经有了很好的实现(ConcurrentHashMap
)。既然我们实际上不需要Map
而是Set
我们可以使用(因为Java 8无论如何)ConcurrentHashMap.newKeySet()
来创建一个并发集,使用与并发地图。
然后,给定并发集,我们几乎可以使用上面的循环,并乐观地删除元素,然后继续搜索失败(这意味着一个线程同时删除了匹配的元素):
class MyCollection<E> {
private Set<E> underlying = ConcurrentHashMap.newKeySet();
void add(E elem) {
underlying.add(elem);
}
@Nullable E findAndRemove(Predicate<E> predicate) {
for (E e : underlying) {
if (predicate.test(e) && remove(e)) {
return e;
}
}
return null;
}
}
与您的示例代码相关的唯一真正变化是我们检查Set.remove()
的结果,以查看该元素是否实际已删除。对于并发集,这可以安全地使用#34; - 也就是说,只有实际删除了对象的线程才会看到true
,所以这个集合只有在实际删除时才会正确返回该元素,然后其他线程就无法返回该元素。
它应该满足您的所有要求并执行以及基础并发映射实现,这在现代JDK上非常好&#34;。
请注意,使用Set
意味着不允许使用重复的元素。从您的描述中可以清楚地知道您是否计划支持重复项,但如果您这样做,则可以使用基于并发多图 1 构建的相同方法,或者仅使用{{1其中ConcurrentHashMap<E, AtomicInteger>
值是具有相同键的元素数量的引用计数,AtomicInteger
和add
方法操纵引用计数 2 。
1 然而,在快速搜索中,我无法找到并发多图的明显开源实现。请注意,您实际上并不需要具有其所有功能的大写M findAndRemove
实现 - 您实际上只需要&#34;某些多重集合&#34;能够添加元素,迭代可用元素,并且&#34;删除&#34;一个元素(即,减少其在集合中的引用计数)。
2 我实际上掩盖了引用计数实现的一些细节,因为在这种情况下,在将refcount递减为零的线程和任何调用的线程之间可能存在争用{ {1}}表示可能将引用计数增加到零以上的相同元素(但该条目已被删除)。这是可以避免的,但我还没有详细说明,因为如果你想支持重复,我不清楚。
答案 1 :(得分:1)
您可以对集合中的变异使用读写锁定,也可以使用ConcurrentHashMap
来表示集合。
Set set =Collections.newSetFromMap(new ConcurrentHashMap<Object,Boolean>());