2012-10-17 44 views
12

在番石榴,有没有一种有效的方式来添加或删除项目到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-简单得多只有我能想到的方法。我错过了什么吗?

+4

你试图解决什么更高水平的问题?也许你不应该处理不可变列表,如果你需要改变它们。 – sjr

+0

你为什么要避免它? –

+0

我知道我可以很容易地解决问题时诉诸可变列表作为inbetween数据持有人,但a)我必须创建多余的中间集合和b)我必须混合java.util集合与guava ImmutableCollections,而我想要粘到一个范例。我想使用ImmutableList,因为我想在getFoos()方法中将它交给客户端,而不会失去控制权,或者不得不创建大量Collections.unmodifiableList包装器对象。 –

回答

7

ConcurrentModificationException与并发和同步并不真正相关。同时访问可变的List可能会破坏它和/或抛出异常(准备好所有3种可能性)。您的代码不能不这样,但随着多线程它不工作之一:

  • 没有同步,没有foosvolatile,有没有保证,另一个线程永远不会看到您所做的更改。
  • 即使有volatile,也可能发生某些更改丢失的情况,例如,当两个线程将项目添加到foos时,它们都可以以原始值开始,然后写入最后一个赢得的项目(并且只有其项目被添加)。

你试图避免的代码是没有什么可以避免的。

  • “我要创建多余的中间集合” - 是的,但有没有免费的午餐:
    • 确定结果提前的大小,这意味着在整个列表
    • 额外的迭代
    • 或分配一个足够大的阵列和复制所需的范围在结果列表中
    • 或分配一个足够大的阵列,并且只使用它(节省了时间和浪费存储器)部分
    • 或创建一个不可改变的视图(节省时间和内存,但后来可能是浪费时间)
  • AFAIK坦率的回答实现了第一个可能性,这是好的,如果谓语是快。
  • “我必须将java.util Collections与guava ImmutableCollections混合使用,而我想坚持一个范例。” - 是的,但为了突变一个集合,需要一个可变集合。 ImmutableList.Builder涵盖了允许以紧凑方式处理它们的最常见情况。

您可能想看看persistent collections,它们针对这些操作进行了优化。但是,您不应该期望持久性列表的速度要快于ArrayListImmutableList

+0

好的,我没有在上面的例子中添加同步,因为它已经足够复杂了,但是比使用例如添加同步更容易。数组列表。仍然:+1持久收藏建议。也许番石榴是不适合寻找这种功能的地方。 –

+1

@SeanPatrickFloyd:Guava团队拒绝复制现有的东西,他们说永久性收集[不太适合](https://groups.google.com/d/msg/guava-discuss/G4E_Hg9GGv0/YFfQf3-AD3IJ )。 – maaartinus

+0

@maaartinus我有一个[问题](http://stackoverflow.com/questions/41925494/how-to-keep-retrying-block-machine-every-x-interval-until-url-is-executed-succes)其中我正在使用番石榴重试,并希望与您核对我的代码是否线程安全,我所做的是正确的方式?没有得到任何答案,所以想与你核对。 – john

13

您可以通过过滤去除,不产生中间ArrayList或制造商,并且只遍历该列表一次:

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

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

+0

我当然想到了这个(对不起,不好意思)。问题是:它删除了每一个事件,而List.remove()只删除了第一个事件,而我正在寻找这个功能。仍然:+1 –

+0

然后我猜你的当前实现中的任何一个都可以达到最佳状态(你可以删除if(index> 0)和if(index