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?
The only valid operations for an 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.
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.");
}
}
Related
I am reading the code of AbstractList where there is a nest class Itr, and it's fine. What confuses me is why Java provide a set of methods to it's Iterator so that it can be used to modify the underlaying collection, such as add(),remove(),set().
What is the essential reason that You Must expose the collection to Iterator? Does it more convenient than modifing a collection by Collection's methods?
private class Itr implements Iterator<E> {
//......bha
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index;
}
public void set(E e) ///...
public void add(E e) //.....
If you directly modify a collection while it's being iterated, you may well get a ConcurrentModificationException because the iterator may become invalid. But if you do it via the iterator, then the iterator's implementation can ensure that it (the iterator) remains valid after the modification.
The iterator code can't do that if it doesn't know about the modification, though, which is why you do it through the iterator rather than directly.
While it would be possible to design collections and iterators such that modifying the collection directly would ensure it kept all iterators up-to-date, it's much more complex and the JDK designers didn't go that route.
Iterable<Position<Integer>> iterable = list.positions();
Iterator<Position<Integer>> iter = iterable.iterator();
while (iter.hasNext()) {
System.out.println(iter.next().getData());
}
The above code works with no issues. list is just an instance of a List class that I wrote. It contains elements of the Integer type.
for (Position<Integer> pos : iterable) {
}
This code fails at the part past the colon. This should be equivalent to the first piece of code, the one with the while loop. So I don't understand why the for-each loop has an error. The error says: "Can only iterate over an array or an instance of java.lang.Iterable" - but iterable already is Iterable, isn't it? What am I missing here?
the following is the full code implementing the aforementioned methods and types.
private class PositionIterator implements Iterator<Position<E>> {
private Position<E> cursor = first();
private Position<E> current = null;
public boolean hasNext() {
return cursor.getData() != null;
}
public Position<E> next() {
if (cursor == null) throw new NoSuchElementException("reached the end of the list");
current = cursor;
cursor = after(cursor);
return current;
}
}
private class PositionIterable implements Iterable<Position<E>> {
public Iterator<Position<E>> iterator() {
return new PositionIterator();
}
}
public Iterable<Position<E>> positions() {
return new PositionIterable();
}
these are nested classes within another class called PositionalList<E>. In the interest of keeping this post compact, I decided to omit the outside class. It's just a bunch of getter and setter methods that are typical for a List class.
public interface Iterable<E> {
public Iterator<E> iterator();
}
^that's the Iterable interface being implemented by PositionIterable
public interface Iterator<E> {
boolean hasNext();
E next();
}
^And that's the Iterator interface.
The enhanced for loop accepts an Iterable, not an Iterator. iter is an Iterator.
Therefore :
for (Position<Integer> pos : iter)
Should be :
for (Position<Integer> pos : iterable)
EDIT : Based on the comments, your problem must be hiding java.lang.Iterable by your custom Iterable interface. If your iterable variable is of the type of your custom Iterable interface, it can't be used by the enhanced for loop, which accepts java.lang.Iterable.
You shouldn't have any issues running that code. Here's my local test code
public static void main(String[] args)
{
Iterable<String> iterable = Arrays.asList("foo",
"bar");
for (String anIterable : iterable)
{
System.out.println(anIterable);
}
}
If you have created a local class or interface called Iterable, that's the only reason I could think why this wouldn't work. If you have done that, delete it and then maybe go back and review the purpose of interfaces too.
I have many instance in code where a Collection, most usually a List is returned from a method and to account for the caller possibly altering that list, a defensive copy is made.
The JDK does not seem to provide a wrapper for this purpose, so I'm trying to roll my own (Note: java.util.concurrent.CopyOnWriteArrayList is not what I'm looking for here).
Schematically, what I'm trying is:
public class CopyOnWriteList<E> extends List<E> {
protected List<E> list;
protected boolean isCopied;
public CopyOnWriteList(List<E> list) {
this.list = list;
}
private void ensureCopy() {
if (!isCopied) {
list = new ArrayList<E>(list);
isCopied = true;
}
}
public E get(int i) {
return list.get(i);
}
public boolean add(E e) {
ensureCopy();
return list.add(e);
}
// ... many more simple methods
}
For most of the API this is simple enough, but looking at java.util.ArrayList I found, that for the method iterator() it returns a different implementation than for listIterator(), although is ListIterator extends its Iterator and it doesn't add any new members.
I wonder if there is a deeper reason behind this - I planned simply to implement iterator() and listIterator() all returning the same type of iterator - a ListIterator. Is there any logical reason why one should not return a ListIterator from the iterator()-method?
ListIterator extends Iterator. So if you return an object that implements ListIterator then it obviously also implements Iterator. But you also can decide to return different object that only implements Iterator, because the implementation could be more effective. The decision is up to you.
Set set = new HashSet();
set.add(1);
set.add(2)
Iterator iter = set.iterator(); // Wher hasnext been overrided
while(iter.hasnext()){
System.out.println(iter.next);
}
Iterator is an interface , set.iterator is returning the Iterator reference .
In HashSet(and its extended ,implemented classes) hasnext is nowhere overriden.
My doubt is where hasnext method is overriden(implemented) and how its linked in this context.
Thanks.
HashSet returns an implementation of Iterator, it doesn't implement Iterator itself. If you look at the source code of HashSet, you can see that it is actually backed by a HashMap:
public HashSet() {
map = new HashMap<E,Object>();
}
and when you call the iterator() method it is the iterator of this map that is returned:
public Iterator<E> iterator() {
return map.keySet().iterator();
}
This map in turn stores a KeySet...
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
Which in turn returns a KeyIterator when its iterator() method is invoked:
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
and the HashIterator implements Iterator:
private abstract class HashIterator<E> implements Iterator<E>
So ultimately, the implementation of Iterator is a private subclass of HashIterator which is hidden away as an implementation detail hidden from clients. Such is the beauty of using interfaces!
You can see in the source of the HashSet that the iterator() method is implemented this way:
public Iterator<E> iterator() {
return map.keySet().iterator();
}
In the class AbstractHashedSet the iterator for keyset is implemented
/**
* KeySet iterator.
*/
protected static class KeySetIterator extends EntrySetIterator {
protected KeySetIterator(AbstractHashedMap parent) {
super(parent);
}
public Object next() {
return super.nextEntry().getKey();
}
}
So you have indeed an implementation.
I guess you should re-phrase your question if you experience a problem with the hasNext() method in your code.
HashSet implementation uses a HashMap so the iterator of HashSet is the iterator of the keySet of its HashMap... the keySet implementation of iterator is in AbstractMap class as seen here: AbstractMap-keySet
In the line
Iterator iter = set.iterator();
You are calling a method on Set which returns an instance of a class which implements the Iterator interface. It doesn't matter exactly what class is returned, all you need to care about is that it implements Iterator, hence implements the hasNext() method.
It's likely that the actual class is not part of the API and might change with different version of the JVM. The only thing you can be sure of is that it will implement Iterator.
private SomeObject[] all_objs;
public Iterator<SomeObject> iterator() {
//
}
What is the best way to get an iterator from an array of SomeObject?
EDIT
So there is no way to generate iterator without using the wrappers like ArrayLists or HashSets ?
You could write your own class that implements Iterator<SomeObject>. Simply implement the methods of the interface. If you don't want the users of the iterator from removing items from the array (since it isn't an ArrayList), then throw an UnsupportedOperationException if this method is called.
Of course if you use an ArrayList<SomeObject> then the iterator() method would only have to return the iterator from the ArrayList making your life much easier.
Using the List iterator:
import java.util.List;
import java.util.Arrays;
public <T> Iterator<T> iterator() {
List<T> list = (List<T>) Arrays.asList(all_objs);
return list.iterator();
}
You can also look at the code of java.util.ArrayList. Here is how you should implement it.
T=SomeObject //Your Class
public YourIterable<T> implements Iterable<T>{
//**Iterable** Implementing this interface allows an object
//to be the target of the "foreach" statement.
public Iterator<T> iterator() {
return new Itr();
}
private class Itr implement Iterator<T> {
//Complex Implementation - You have to maintain a Cursor to keep track of
// which record was accessed
boolean hasNext() {
//Returns true if the iteration has more elements.
}
T next() {
//Returns the next element in the iteration.
}
void remove() {//Remove
}
}
}