icn
icn

Reputation: 17876

Implement Java Iterator and Iterable in same class?

I am trying to understand Java Iterator and Iterable interfaces

I am writing this class

class MyClass implements Iterable<String> {
    public String[] a = null;
    public MyClass(String[] arr) {
        a = arr;    
    }

    public MyClassIterator iterator() {
        return new MyClassIterator(this);
    }

    public class MyClassIterator implements Iterator<String> {
        private MyClass myclass = null;
        private int count = 0;
        public MyClassIterator(MyClass m) {
            myclass = m;    
        }

        public boolean hasNext() {
            return count < myclass.a.length;
        }
        public String next() {
            int t = count;
            count++;
            return myclass.a[t];
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }   
}

It seems to be working.

Should I have:

Myclass implements Iterable<Stirng>, Iterator<String> {

}

Or I should put MyClassIterator outside MyClass as

class MyClass implements Iterable<String> {
    public String[] a = null;
    public MyClass(String[] arr) {
        a = arr;    
    }
    public MyClassIterator iterator() {
        return new MyClassIterator(this);
    }
}


    public class MyClassIterator implements Iterator<String> {
        private MyClass myclass = null;
        private int count = 0;
        public MyClassIterator(MyClass m) {
            myclass = m;    
        }

        public boolean hasNext() {
            return count < myclass.a.length;
        }
        public String next() {
            int t = count;
            count++;
            return myclass.a[t];
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }   

Which one is better?

Upvotes: 16

Views: 41246

Answers (3)

Stephen Swensen
Stephen Swensen

Reputation: 22297

You were on track with your first try. MyClass only needs to implement Iterable<String>, which in turn requires you to provide an Iterator<String> implementation to return from Iterable<String>.iterator().

There's no need to put the MyClassIterator outside of MyClass because in most cases you will never even need to directly use the Iterator<String> (it's used implicitly by the for .. in .. syntax on Iterable<String>s), and in all other cases the interface is sufficient unless you actually add additional behavior to the implementation (which you likely won't ever need to do).

Here's how I'd do it, see comments inlined:

import java.util.Iterator;

class MyClass implements Iterable<String>{
    public String[] a=null; //make this final if you can
    public MyClass(String[] arr){
        a=arr; //maybe you should copy this array, for fear of external modification
    }

    //the interface is sufficient here, the outside world doesn't need to know
    //about your concrete implementation.
    public Iterator<String> iterator(){
        //no point implementing a whole class for something only used once
        return new Iterator<String>() {
            private int count=0;
            //no need to have constructor which takes MyClass, (non-static) inner class has access to instance members
            public boolean hasNext(){
                //simplify
                return count < a.length;
            }
            public String next(){
                return a[count++]; //getting clever
            }

            public void remove(){
                throw new UnsupportedOperationException();
            }
        };
    }
}

Upvotes: 9

Mike Samuel
Mike Samuel

Reputation: 120516

You should not do Myclass implements Iterable<String>,Iterator<String>{ since iterators are single-use. With the exception of list iterators, there's no way to return them to the start.

Incidentally, you can skip the

MyClass myClass;
public MyClassInterator(MyClass m){
  myclass=m;  
}

and instead of referencing

myClass

reference

MyClass.this

Your inner class is not static, so MyClass.this will reference the instance of the enclosing class that created it.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1500585

You should almost never implement both Iterable and Iterator in the same class. They do different things. An iterator is naturally stateful - as you iterate using it, it has to update its view of the world. An iterable, however, only needs to be able to create new iterators. In particular, you could have several iterators working over the same original iterable at the same time.

Your current approach is pretty much okay - there are aspects of the implementation I'd change, but it's fine in terms of the separation of responsibilities.

Upvotes: 35

Related Questions