Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why in Guava filter/transform functions sometimes return modifiable view and some returns unmodifiable view?

Tags:

java

guava

For example all Lists, Collections2, Sets return a modifiable view - removing from view collection will remove original items.

This works fine:

List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5, 6, -1, -2, -3, -4);    
Collection<Integer> transform = Collections2.filter(
    list, new Predicate<Integer>() {
        public boolean apply(Integer input) {
            return input.intValue() > 0;
        }
    });
transform.clear();

When I use Iterables and Iterators methods filter/transform I get umodifiable view (i.e all this code reuse UnmodifibleIterator).

This doesn't work:

List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5, 6, -1, -2, -3, -4);
Iterable<Integer> transform = Iterables.filter(
    list, new Predicate<Integer>() {
        public boolean apply(Integer input) {
            return input.intValue() > 0;
        }
    });
Iterables.removeIf(transform, Predicates.<Object>alwaysTrue());

I can't find any semantic difference between Iterable and Collection/List/Set/Map so why there are so different implementations in Guava?

The other strange behavior is that the iterator in the first case still doesn't allow the remove operation, but clear or remove/removeAll works fine.

like image 390
yura Avatar asked Jul 15 '11 12:07

yura


1 Answers

Iterators.transform (and Iterables.transform, by extension) does support remove(). From its Javadoc:

The returned iterator supports remove() if the provided iterator does.

Iterators.filter, however, does not. The reason for this is that there is no way for a filtered iterator to implement hasNext() without calling next() on the underlying iterator. Calling hasNext() on the underlying iterator is not enough, because the next element in that iterator (and every one after that as well, possibly) may not match the Predicate.

The problem, then, is that calling hasNext() on the filtered iterator must advance the position of the underlying iterator. This prevents a subsequent call to remove() from removing the element that was returned by the most recent call to next() (which is part of the contract of remove()). Thus, remove() cannot be supported on a filtered iterator.

The Iterator for a filtered Collection has the exact same issue (in fact, it's created using Iterators.filter). The clear() and removeAll methods work because they have full control over the iterator (they are both implemented using Iterables.removeIf).

like image 97
ColinD Avatar answered Nov 19 '22 18:11

ColinD