In Guava, is there an efficient way to add or remove items to an ImmutableList (creating new Lists in the process, of course).
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?
The
ConcurrentModificationExceptionisn’t really related to concurrency and synchronization. Accessing a mutableListconcurrently 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:foosbeingvolatile, there’s no guarantee that another thread will ever see the changes you’ve made.volatile, it can happen that some changes get lost, e.g., when two threads add an item tofoos, both of them can start with the original value and then the one writing last wins (and only its item gets added).The code you’re trying to avoid is nothing to be avoided.
ImmutableList.Buildercovers just the most common cases allowing to handle them in a compact way.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
ArrayListorImmutableList.