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.