Generics problem with iterator... (I am a generics noob)

Hello. G-noob here.

I have created a Set class which implements java.lang.Iterable and uses generics to specify what type of element it will accept. However the accompanying Iterator does not seem to work as I expected. I know I am missing something in the use of generics... but I don't know where. For instance (the example I use in the code below), if I create a Set<Integer> object named someSet, and try to iterate through it with a foreach loop, I am not allowed to assume that the elements of the set are Integer objects. That is, I must use for(Object i : someSet) instead of for(Integer i : someSet).

What am I doing wrong? It seems to me that Set.Iterator.next() should return an object of type E, not of type Object. Thank you very much for any help and suggestions.

Here is my code. The main method is at the bottom.

import java.util.NoSuchElementException;

publicclass Set<Eextends Object>implements Iterable{

private Position head;

privateint size;

publicboolean isEmpty(){return head ==null;}

publicboolean contains(E element){return getPosition(element) !=null;}

public Set.Iterator iterator(){returnnew Set.Iterator(head);}

private Position getPosition(Object element){

PositionIterator positions =new PositionIterator(head);

Position current;

while (positions.hasNext()){

current = (Position)positions.next();

if (current.element == element)return current;

}

returnnull;

}

publicvoid add(E element){

if (!contains(element)){

if (isEmpty()) head =new Position(element);

else head.prev = head.prev.next =new Position(element, head.prev, head);

size++;

}

}

publicvoid remove(E element){

if (contains(element)){

if (size == 1) head =null;

else{

Position remove = getPosition(element);

if (remove == head) head = head.next;

remove.prev.next = remove.next;

remove.next.prev = remove.prev;

remove.prev = remove.next =null;

}

size--;

}

}

privateclass Position{

private E element;

private Position prev, next;

public Position(E element){

this.element = element;

prev =this;

next =this;

}

public Position(E element, Position prev, Position next){

this.element = element;

this.prev = prev;

this.next = next;

}

}

class Iteratorextends PositionIterator{

public Iterator(Position start){super(start);}

public E next(){return ((Position)super.next()).element;}

}

privateclass PositionIteratorimplements java.util.Iterator{

private Position current;

private Position start;

privateboolean started;

private PositionIterator(Position start){

this.start = start;

current = start;

started =false;

}

publicboolean hasNext(){return !(start ==null || (started && current == start));}

public Object next(){

if (hasNext()){

Position temp = current;

current = current.next;

started =true;

return temp;

}

elsethrownew NoSuchElementException("No more elements");

}

publicvoid remove(){thrownew UnsupportedOperationException("Cannot remove Set elements via an Iterator.");}

}

publicstaticvoid main(String[] args){

Set<Integer> s =new Set<Integer>();

s.add(3);

s.add(19);

s.add(7);

// This works:

for (Object i : s) System.out.print(i +"; ");

// But this does not:

//for (Integer i : s) System.out.print(i + "; ");

}

}

[7945 byte] By [ProGrapea] at [2007-11-27 4:36:03]
# 1

> public class Set<E extends Object> implements Iterable {

This is redundant. Replace with

public class Set<E> implements Iterable {

because all classes extend Object.

> public Set.Iterator iterator() { return new Set.Iterator(head); }

Try replacing this with

public Set<E>.Iterator iterator() { return new

Set<E>.Iterator(head); }

I think this is the foul code.

di_gamaa at 2007-7-12 9:46:12 > top of Java-index,Core,Core APIs...
# 2

Hhmmm I hadn't thought of doing that. However, it does not seem to do the trick.

For whatever reason,return new Set<E>.Iterator(head);

is considered bad code (by the IDE), and usingpublic Set<E>.Iterator iterator() {

return new Set.Iterator(head);

}

is valid but does not solve the problem.

Is there no rhyme or reason to it?

ProGrapea at 2007-7-12 9:46:12 > top of Java-index,Core,Core APIs...
# 3

First of all, why are you exposing your package-private Iterator class instead of the according Iterator interface of java.util?

The problematic code to me seems to be that PositionIterator implements Iterator and not Iterator<E>. Hence you should change that and let iterator() return java.util.Iterator<E>, too.

stefan.schulza at 2007-7-12 9:46:12 > top of Java-index,Core,Core APIs...
# 4
Well, you have pointed out two more mistakes I had made... so I returned a java.util.Iterator, and implemented Iterator<E>, but still no luck. I am still unabe to iterate through the Set<Integer> as Integer objects.aarrrgh!
ProGrapea at 2007-7-12 9:46:12 > top of Java-index,Core,Core APIs...
# 5

There's another one: your Set should implement Iterable<E>

Could you repost your new code? The above solution has a problem with subclassing PositionIterator as Iterator on single elements. Maybe, you should have a look at the source of HashMap, where a generic HashIterator is reused for values, keys and entries.

stefan.schulza at 2007-7-12 9:46:12 > top of Java-index,Core,Core APIs...
# 6
You will find all this a lot easier if you just extend AbstractSet<E> and follow the instructions in its javadoc as to what to override.
ejpa at 2007-7-12 9:46:12 > top of Java-index,Core,Core APIs...
# 7

Hullo, well, having Set<E> extend Iterable<E> has done the trick! Thank you very much!

Let me show you the working version. Actually, there are two working versions. I'm only showing those parts of code which are different.

In this version, only Set.Iterator implements java.util.Iterator<E>. I was not able to have Set.PositionIterator implement java.util.Iterator<Position> as well, because this causes a conflict with its subclass Set.Iterator (Set.Iterator cannot implements both java.util.Iterator<E> and java.util.Iterator<Position>). Also, the return type for Set.PositionIterator.next() must be Object so that Set.Iterator can override it using return type E.

==================================================public class Set<E> implements Iterable<E> {

public java.util.Iterator<E> iterator() { return new Set.Iterator(head); }

class Iterator extends PositionIterator implements java.util.Iterator<E> {

public Iterator(Position start) { super(start); }

public E next() { return ((Position)super.next()).element; }

}

private class PositionIterator {

private Position current;

private Position start;

private boolean started;

private PositionIterator(Position start) {

this.start = start;

current = start;

started = false;

}

public boolean hasNext() { return !(start == null || (started && current == start)); }

public Object next() {

if (hasNext()) {

Position temp = current;

current = current.next;

started = true;

return temp;

}

else throw new NoSuchElementException("No more elements");

}

public void remove() { throw new UnsupportedOperationException("Cannot remove via an Iterator."); }

}

}

==================================================

This next version arose out of me realising through experimentation that only specifying the parameter for Iterable is necessary to give a correctly working for loop. Now, Set.PositionIterator implements java.util.Iterator (with no parameter). I think this one is better because the requirements of java.util.Iterator are enforced for both Set.Iterator and Set.PositionIterator, and because it does away with the unnecessary usage of a parameter in the above.

==================================================public class Set<E> implements Iterable<E> {

public java.util.Iterator<E> iterator() { return new Set.Iterator(head); }

class Iterator extends PositionIterator implements java.util.Iterator<E> {

public Iterator(Position start) { super(start); }

public E next() { return ((Position)super.next()).element; }

}

private class PositionIterator {

private Position current;

private Position start;

private boolean started;

private PositionIterator(Position start) {

this.start = start;

current = start;

started = false;

}

public boolean hasNext() { return !(start == null || (started && current == start)); }

public Object next() {

if (hasNext()) {

Position temp = current;

current = current.next;

started = true;

return temp;

}

else throw new NoSuchElementException("No more elements");

}

public void remove() { throw new UnsupportedOperationException("Cannot remove via an Iterator."); }

}

}

==================================================

ProGrapea at 2007-7-12 9:46:12 > top of Java-index,Core,Core APIs...
# 8
Thanks for the suggestion ejp, I'll look into that.
ProGrapea at 2007-7-12 9:46:12 > top of Java-index,Core,Core APIs...