1

If I have a Map or a List and get an iterator from it, for example:

var map = new HashMap();
var iterator = map.entrySet().iterator();

if something modifies that map after the fact, will it affect the iterator or is the iterator basically immutable once created?

If it's not immutable, how to create an immutable iterator?

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • 1
    related: https://stackoverflow.com/questions/21081318/can-a-iterator-change-the-collection-it-is-iterating-over-java? – Book Of Zeus Feb 21 '19 at 02:30
  • related but my question is probably better worded and more to the point – Alexander Mills Feb 21 '19 at 02:32
  • 1
    why -1? I think that is an interesting question? – Book Of Zeus Feb 21 '19 at 02:40
  • 1
    Please **read the documentation**, i.e. the javadoc of [`HashMap`](https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html): *"The iterators returned by all of this class's "collection view methods" are **fail-fast**: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own `remove` method, the iterator will throw a `ConcurrentModificationException`."* – Andreas Feb 21 '19 at 02:40
  • OK let's think outside the box - how to create an immutable iterator? Come on people. The documentation might be clear if you dig, but I am looking for a way to create an immutable iterator once it's created. – Alexander Mills Feb 21 '19 at 02:42
  • Updated the OP for those who can't read between the lines... – Alexander Mills Feb 21 '19 at 02:44
  • You would, I think, need to design a `Collection` from the ground up with an eye toward supporting these so-called "immutable" iterators. A collection instance would need to be aware at all times of every `Iterator` instance in existence and be able to notify those `Iterator` instances of every structural modification in sufficient detail that the `Iterator` could figure out how to proceed. And, of course, the `Iterator`s themselves would have to be sufficiently resourceful to make sense of any possible change notification that might be thrown their way. – Kevin Anderson Feb 21 '19 at 02:58
  • 4
    The question as phrased doesn't really make sense, as iterators are inherently necessarily mutable; if I understand correctly, you want an iterator that's a snapshot of the underlying collection at some moment in time? – chrylis -cautiouslyoptimistic- Feb 21 '19 at 03:18
  • Also, see the documentation for `CopyOnWriteArrayList`, which I think implements what you're asking for. – chrylis -cautiouslyoptimistic- Feb 21 '19 at 03:19
  • @chrylis CopyOnWriteArrayList makes a new copy for every add() operation, etc, that's not quite what I am looking for – Alexander Mills Feb 21 '19 at 04:54
  • @chrylis Iterators are not more "inherently necessarily mutable" as a concept than collections (containers). – Raedwald Feb 21 '19 at 17:37
  • @chrylis @chrylis Consider that C++ has `const_iterator` types (for referring to `const` values) and can have `const iterator` values (for iterators that can not be incremented). – Raedwald Feb 21 '19 at 17:49
  • 1
    @Raedwald This is a question relating to Java and the term should be interpreted in its context. Your `const iterator` does not match the description in the question. – chrylis -cautiouslyoptimistic- Feb 21 '19 at 21:02

2 Answers2

2

An iterator instance itself would not generally need the concept of mutability/immutability. However, the collection it is iterating over may be intended to be unmodifiable or immutable. Here is a way to disable the ability of an iterator to change the collection. If you return an instance of this class, then the remove() method of the returned iterator is disabled. The class is used by returning UnmodifiableIterator.create(yourIterator).

import java.util.Iterator;

/**
 * UnmodifiableIterator, A wrapper around an iterator instance that
 * disables the remove method.
 */
public final class UnmodifiableIterator<E> implements Iterator<E> {

    /**
     * iterator, The base iterator.
     */
    private final Iterator<? extends E> iterator;

    private UnmodifiableIterator(final Iterator<? extends E> iterator) {
        this.iterator = iterator;
    }

    public static <E> Iterator<E> create(final Iterator<? extends E> iterator) {
        if (iterator == null) {
            throw new NullPointerException("The iterator can not be null.");
        }
        return new UnmodifiableIterator<>(iterator);
    }

    @Override
    public boolean hasNext() {
        return iterator.hasNext();
    }

    @Override
    public E next() {
        return iterator.next();
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Iterator.remove() is disabled.");
    }

}
BlakeTNC
  • 941
  • 11
  • 14
  • 1
    This code is pretty much the same as Apache common collection's UnmodifiableIterator. Ref: https://commons.apache.org/proper/commons-collections/jacoco/org.apache.commons.collections4.iterators/UnmodifiableIterator.java.html – cquezel Sep 08 '22 at 13:33
1

The only valid operations for a read-only iterator are increment and dereference (although the Java Iterator combines the two in its next() method). If you have an unmodifiable iterator, that leaves you only dereferencing. The dereferencing can either give you an object reference, or it can be not valid to dereference because it does not refer to a valid position in the collection.

But those are the same semantics as Optional: an Optional can either be empty or have a valid object reference. So, create an Optional from the Iterator and use that as your "unmodifiable iterator":

private Optional<T> unmodifiableNext(Iterator<T> i)
{
   if (i.hasNext()) {
      return Optional.ofNullable(i.next());
   } else {
      return Optional.empty();
   }
}

This has the additional benefit that the Optional is no longer tied to the collection, so the collection can be safely changed without changing which object the Optional refers to.

Raedwald
  • 46,613
  • 43
  • 151
  • 237