views:

172

answers:

4

Hi all,

I have two data structures in Java:
One is called DebateAssignment and has 5 DebateTeam objects, each associated with a specific enum that includes

{JUDGE, PROP1, PROP2, OP1, OP2}

In another class I use List<DebateAssignment> and I want to create an iterator that will point to a specific DebateTeam in a specific DebateAssignment and I want it to iterate over all teams over all assignments, going from an assignment to assignment seamlessly.

How would I go about doing that?

A: 

You can extend a list implementation like ArrayList and add a method to return an Iterator implementation which does what you want.

Overriding the iterator() method may not be an option doe to the generic type of the iterator returned by it.

Chandru
Actually I didn't understand what he really need but anyway extending collections is a bad practice. You can create a wrapper class around ArrayList and decorate some methods like iterator() (i.e. it's a Decorator pattern).
Roman
If he really wants a first class List I believe it is significantly simpler to go with extension because for composition he'd have to implement all the List methods and delegate them to the actual List.I do agree that the requirement is pretty weird.
Chandru
+4  A: 

Assuming DebateAssignment has something like

public Collection<DebateTeam> getDebateTeams();

You want an Iterator<DebateTeam>?

If so, would you want something like:

public class DebateTeamIterator implements Iterator<DebateTeam> {
    private Iterator<DebateAssignment> iAssignment;
    private Iterator<DebateTeam> iTeam;

    public DebateTeamIterator(Iterator<DebateTeam> iAssignment) {
        this.iAssignment = iAssignment;
        if (iAssignment.hasNext())
            iTeam = iAssignment.next().getDebateTeams().iterator();
        else
            iTeam = new LinkedList<DebateTeam>().iterator();
    }

    public boolean hasNext() {
       return iTeam.hasNext() || iAssignment.hasNext();
    }

    public DebateTeam next() {
        if (!iTeam.hasNext())
            iTeam = iAssignment.next().getDebateTeams().iterator();
        return iTeam.next();
    }

    // ... other methods removed for brevity...
}
Clinton
I can easily be accused of extreme bias, but I don't think it's good advice to suggest pasting this many lines of code, that are this nontrivial and unproven, as opposed to (you guessed it) using a library that already sunk many person-days into exhaustively testing and fixing their implementation.
Kevin Bourrillion
Thanks! Never implemented an iterator before, this answer was really helpful!
Amir Rachum
+2  A: 

Probably the easiest approach is:

List<DebateAssignment> list = ...
List<DebateTeam> dtList = new ArrayList<DebateTeam>();
for (DebateAssignment da : list) {
    dtList.addAll(da.getTeams());
}
return dtList.iterator();

Of course you could write a new class that implements Iterator<DebateTeam> that "flattens" nested iterators, but that is somewhat involved, as you will have to explicitly keep track of both iterators ... (see Clinton's answer for details on this).

meriton
+3  A: 

One way, using google-collections / guava:

return Iterables.concat(Iterables.transform(assignments,
    new Function<DebateAssigment, Collection<DebateTeam>>() {
      public Collection<DebateTeam> apply(DebateAssignment assignment) {
        return assignment.getDebateTeams();
      }
    }));

Another way is to store the data as a Multimap<DebateAssignment, DebateTeam>, then simply iterate over either the values() or entries() view. That data structure won't model the JUDGE/PROP1/etc. association, though.

Kevin Bourrillion