java 向 Guava ImmutableList 添加和删除项目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12937938/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 10:51:24  来源:igfitidea点击:

Adding and removing items to a Guava ImmutableList

javaguava

提问by Sean Patrick Floyd

In Guava, is there an efficient way to add or remove items to an ImmutableList(creating new Lists in the process, of course).

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

The simplest way I can come up with is this:

我能想到的最简单的方法是:

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();
    }
}

What I would like to avoid doing is this:

我想避免做的是:

public void removeFoo(final String foo) {
    final ArrayList<String> tmpList = Lists.newArrayList(this.foos);
    if(tmpList.remove(foo))this.foos=ImmutableList.copyOf(tmpList);
}

But unfortunately it is so much simpler than any Guava-only method I can think of. Have I missed something?

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

采纳答案by maaartinus

The ConcurrentModificationExceptionisn't really related to concurrency and synchronization. Accessing a mutable Listconcurrently might corrupt it and/or throw the exception (be prepared to all 3 possibilities). You code can't fail this way, but with multithreading it doesn't work either:

ConcurrentModificationException是不是真的与并发性和同步。同时访问一个可变List变量可能会破坏它和/或抛出异常(准备好所有 3 种可能性)。您的代码不会以这种方式失败,但使用多线程它也不起作用:

  • Without synchronization and without foosbeing volatile, there's no guarantee that another thread will ever see the changes you've made.
  • Even with volatile, it can happen that some changes get lost, e.g., when two threads add an item to foos, both of them can start with the original value and then the one writing last wins (and only its item gets added).
  • 没有同步和foosbe volatile,不能保证另一个线程会看到你所做的更改。
  • 即使使用volatile,也可能会丢失一些更改,例如,当两个线程向 中添加一个项目时foos,它们都可以从原始值开始,然后最后写入的那个获胜(并且只有它的项目被添加)。

The code you're trying to avoid is nothing to be avoided.

您试图避免的代码是无可避免的。

  • "I have to create superfluous intermediate collections"- yes, but there's no free lunch:
    • determine the size of the result in advance, which means an additional iteration through the whole list
    • or allocate a large enough array and copy the needed range in the resulting list
    • or allocate a large enough array and use only part of it (saving time and wasting memory)
    • or create an immutable view (saving both time and memory, but possibly losing time later)
  • AFAIK Frank's answer implements the first possibility, which is fine if the predicate is fast.
  • "I have to mix java.util Collections with guava ImmutableCollections, while I'd like to stick to one paradigm."- yes, but for mutating a collection a mutable collection is needed. The ImmutableList.Buildercovers just the most common cases allowing to handle them in a compact way.
  • “我必须创建多余的中间系列”——是的,但天下没有免费的午餐:
    • 预先确定结果的大小,这意味着对整个列表进行额外的迭代
    • 或分配一个足够大的数组并在结果列表中复制所需的范围
    • 或者分配一个足够大的数组并只使用其中的一部分(节省时间并浪费内存)
    • 或者创建一个不可变的视图(节省时间和内存,但可能会浪费时间)
  • AFAIK Frank's answer实现了第一种可能性,如果谓词很快,这很好。
  • “我必须将 java.util Collections 与 guava ImmutableCollections 混合使用,而我想坚持一种范式。” - 是的,但是为了改变集合,需要一个可变集合。在ImmutableList.Builder仅涉及最常见的情况下允许处理它们以紧凑的方式。

You might want to have a look at persistent collections, which are optimized for such operations. However, you shouldn't expect e.g. the persistent list to be as fast as an ArrayListor ImmutableList.

您可能想查看针对此类操作进行了优化的持久集合。但是,您不应该期望持久列表与ArrayListor一样快ImmutableList

回答by Frank Pavageau

You can remove by filtering, which doesn't create an intermediate ArrayListor builder, and only traverses the list once:

您可以通过过滤删除,这不会创建中间ArrayList或构建器,并且只遍历列表一次:

public void removeFoo(final String foo) {
    foos = ImmutableList.copyOf(Collections2.filter(foos,
            Predicates.not(Predicates.equalTo(foo)));
}

For adding, I don't see a better solution.

对于添加,我没有看到更好的解决方案。