从SynchronizedMultimap中删除元素会添加意外行为

时间:2016-09-02 09:00:24

标签: java collections lambda java-8 guava

我正在使用Guava 18.0的同步SetMultimap

该集合声明如下

private SetMultimap<String, Foo> fooMultimap;

private StatusService() {
    this.fooMultimap = Multimaps.synchronizedSetMultimap(HashMultimap.<String, Foo>create());
}

StatusService是一个弹簧引导@Service,当然它被视为单例。 SetMultimap是一个包含的多图 其中Foo是具有多个属性的模型。

因此我有以下方法:

/* remove element */
public void removeFoo(String fooId, Foo foo) {
    fooMultimap.remove(fooId, foo);
}

以及

/* remove element by unique property(element path) */
public void removeByPath(String path) {
    Lists.newArrayList(getAllFoos().values()).stream()
                .filter(Objects::nonNull)
                .filter(foo -> Optional.ofNullable(foo.getPath())
                        .filter(fooPath -> fooPath.contains(path))
                        .isPresent()
                ).forEach(filteredFoo -> removeFoo(img.getId(), img));
  }
}

和getAllFoos()方法如下

public SetMultimap<String, Foo> getAllFoos() {
    return this.fooMultimap;
}

我无法理解的是为什么Guava没有从多图中删除元素,也许我做错了什么,我尝试了所有可能的组合.values().asMap()或{{ 1}}没有任何运气。

有人可以解释一下从番石榴.entries()中删除关键值条目的最佳方法是什么?

这是一个重现我的问题的例子,如果我改变一个对象,那么我就不能再删除条目了。当我改变图像的状态时会发生改变

synchronized SetMultimap

1 个答案:

答案 0 :(得分:1)

由于Multimap<K,V>是一种Map<K,Set<V>>(虽然没有实现它),但它具有相似的语义,即您不能在插入后以影响其hashcode / equality的方式修改元素。< / p>

除此之外,你遇到了iterate-then-lookup反模式。这不仅效率低,而且在与迭代器一起使用时会导致ConcurrentModificationException,迭代器不支持对它们正在迭代的集合的修改(除了那些通过迭代器本身进行的修改)。

在大多数情况下,有简单的替代方案,例如而不是

public static void removeByPath(String path) {
    mm.get("2").stream()
            .filter(Objects::nonNull)
            .filter(image -> Optional.ofNullable(image.getPath())
                    .filter(imagePath -> imagePath.contains(path)
                                      && image.getStatus().equals(ImageState.DONE))
                    .isPresent()
            ).forEach(img -> {
        mm.values().remove(img);
    });
}

您只需使用

即可
public static void removeByPath(String path) {
    mm.get("2").removeIf(
        image -> Optional.ofNullable(image).map(Image::getPath)
                .filter(imagePath -> imagePath.contains(path)
                                  && image.getStatus().equals(ImageState.DONE))
                .isPresent()
        );
}

避免过时的查找,不会抛出ConcurrentModificationException。请注意,虽然现在可以使用,但即使哈希代码已更改,在插入后更改哈希代码仍然不是正确的用法,并且后续查找可能会失败的事实会让您感到惊恐。