May Sohatchevzzki
May Sohatchevzzki

Reputation: 25

How can I change my Iterator to be a Fast-fail Iterator?

I'm trying to understand how to modify the Iterator of my Linked List to be fail-fast.

I understand what it means for an Iterator to be fail-fast, but I don't know how exactly should I change my implementation to achieve this behavior.

I was thinking about adding a key so for every add, but I don't think it is good enough. It is the first time I'm trying to implement a fail-fast iterator, and I would really appreciate any hint.

My code:

package GenericLinkedList;

import java.util.Iterator;

public class GenericLinkedList<E> implements Iterable<E> {
    private Node<E> head;

    public void pushFront(E data){
        head = new Node<>(data, head);
    }

    public E popFront(){
        E dataHead = head.data;
        head = head.next;

        return dataHead;
    }

    public int size(){
        int counter = 0;

        for(E element : this){
            ++counter;
        }

        return counter;
    }

    public boolean isEmpty(){
        return (null == head);
    }

    public Iterator<E> find(E data){
        IteratorIMP iter = new IteratorIMP(head);

        for(E element : this){
            if(element.equals(data)){
                return iter;
            }
            iter.next();
        }

        return null;
    }

    public static <E> GenericLinkedList<E> merge(GenericLinkedList<E> firstList, GenericLinkedList<E> secondList){
        GenericLinkedList<E> mergeList = new GenericLinkedList<E>();

        for(E element : firstList){
            mergeList.pushFront(element);
        }

        for(E element : secondList){
            mergeList.pushFront(element);
        }

        return newReverse(mergeList);
    }

    public static <E> GenericLinkedList<E> newReverse(GenericLinkedList<E> list){
        GenericLinkedList<E> reverseList = new GenericLinkedList<E>();

        for(E element : list){
            reverseList.pushFront(element);
        }

        return reverseList;
    }

    private static class Node<T> {
        private Node<T> next;
        private T data;

        private Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }
    }

    private class IteratorIMP implements Iterator<E> {

        Node<E> node;

        private IteratorIMP(Node<E> start){
            node = start;
        }

        @Override
        public boolean hasNext() {
            return (node != null);
        }

        @Override
        public E next() {
            E data = node.data;
            node = node.next;

            return data;
        }
    }

    @Override
    public Iterator<E> iterator() {
        IteratorIMP iter = new IteratorIMP(head);

        return iter;
    }
}

Upvotes: 1

Views: 99

Answers (1)

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 29028

Fail-fast implementation of the Iterator insures that it would raise an exception if there's an evidence that the collection is being structurally modified (an element was added or removed) during the iteration process, because in such a case, iterator might not be able to reflect the actual state of the collection.

The common approach is to introduce two counters of modifications:

  • one as the instance field of the list modCount, which would be incremented whenever list gets modified via pushFront(), popFront(), etc.
  • another in the Iterator - expectedModCount.

If during the next() call turns out that modCount and expectedModCount variables are not equal, that means that there has been a modification after the iterator was created. In such a case, fail-fast Iterators from the JDK would throw ConcurrentModificationException.

public class GenericLinkedList<E> implements Iterable<E> {
    private Node<E> head;
    private int modCount;
    
    public void pushFront(E data) {
        modCount++; // list is being modified - incrementing the count
        head = new Node<>(data, head);
    }
    
    public E popFront() {
        modCount++; // list is being modified - incrementing the count
        E dataHead = head.data;
        head = head.next;
        
        return dataHead;
    }
    
    // ...
    
    private class IteratorIMP implements Iterator<E> {
        
        private Node<E> node;
        private final int expectedModCount;
        
        private IteratorIMP(Node<E> start) {
            this.node = start;
            this.expectedModCount = GenericLinkedList.this.modCount;
        }
        
        // ...
        
        @Override
        public E next() {
            if (expectedModCount != modCount) {
                throw new ConcurrentModificationException();
            }
            
            E data = node.data;
            node = node.next;
            
            return data;
        }
    }
}

Upvotes: 1

Related Questions