在 Guava 中,有一种向ImmutableList添加或删除项目的有效方法(当然,可以在此过程中创建新列表)。

我想出的最简单的方法是:

private ImmutableList<String> foos = ImmutableList.of();

public void addFoo(final String foo) {
    if (this.foos.isEmpty()) {
        foos = ImmutableList.of(foo);
    } else {
        foos = ImmutableList.<String>builder().addAll(foos).add(foo).build();
    }
}

public void removeFoo(final String foo) {
    final int index = this.foos.indexOf(foo);
    if (index > -1) {
        final Builder<String> builder = ImmutableList.<String>builder();
        if (index > 0) builder.addAll(this.foos.subList(0, index));
        final int size = this.foos.size();
        if (index < size - 1) builder.addAll(this.foos.subList(index+1, size));
        this.foos = builder.build();
    }
}

我想避免这样做的是:
public void removeFoo(final String foo) {
    final ArrayList<String> tmpList = Lists.newArrayList(this.foos);
    if(tmpList.remove(foo))this.foos=ImmutableList.copyOf(tmpList);
}

但不幸的是,它比我能想到的任何仅使用 Guava 的方法都要简单得多。我错过了什么吗?

最佳答案

ConcurrentModificationException与并发和同步并没有真正的关系。同时访问可变的List可能会破坏它和/或引发异常(请为所有3种可能性做好准备)。您的代码不能以这种方式失败,但是在多线程中它也不起作用:

  • 如果没有同步并且没有foosvolatile,则不能保证另一个线程将看到您所做的更改。
  • 即使使用volatile,也可能会丢失一些更改,例如,当两个线程将一个项目添加到foos时,两个线程都可以从原始值开始,然后最后写入的那个赢(仅添加其项目)。

  • 您要避免的代码是无可避免的。
  • “我必须创建多余的中间集合”-是的,但是没有免费的午餐:
  • 预先确定结果的大小,这意味着整个列表
  • 会进行额外的迭代
  • 或分配足够大的数组,然后在结果列表
  • 中复制所需的范围
  • 或分配足够大的数组并仅使用其中的一部分(节省时间并浪费内存)
  • 或创建一个不变的 View (节省时间和内存,但可能会在以后浪费时间)
  • AFAIK弗兰克的答案实现了第一种可能性,如果谓词很快,那就很好。
  • “我必须将java.util集合与guava ImmutableCollections混合使用,而我想坚持一种范例。” -是的,但是要使一个集合变异,就需要一个可变的集合。 ImmutableList.Builder仅涵盖允许以紧凑方式处理它们的最常见情况。

  • 您可能想看看persistent collections,它已针对此类操作进行了优化。但是,您不应期望例如持久列表与ArrayListImmutableList一样快。

    07-24 09:39
    查看更多