Suppose I have this:
public class Unit {
...
List mobileSuits;
List pilots;
...
}
Basically, assume MobileSuit and Pilot need to be separated.
That's fine, but here you're trying to treat them as a unit, so structure your code that way. The suggestions above use a Pair class or Map.Entry, but it's much better to provide a clearly-named object that represents a MobileSuit with a Pilot, e.g.:
public class OccupiedSuit {
private final MobileSuit suit;
private final Pilot pilot;
public OccupiedSuit(MobileSuit suit, Pilot pilot) {
this.suit = checkNotNull(suit);
this.pilot = checkNotNull(pilot);
}
// getters, equals, hashCode, toString
// or just use @AutoValue: https://github.com/google/auto/tree/master/value
}
Then, rather than constructing a custom Iterator/Iterable, just write a helper function that zips up the two lists. For example:
public static List assignPilots(
Iterable suits, Iterable pilots) {
Iterator suitsIter = suits.iterator();
Iterator pilotsIter = pilots.iterator();
ImmutableList.Builder builder = ImmutableList.builder();
while (suitsIter.hasNext() && pilotsIter.hasNext()) {
builder.add(new OccupiedSuit(suitsIter.next(), pilotsIter.next()));
}
// Most of the existing solutions fail to enforce that the lists are the same
// size. That is a *classic* source of bugs. Always enforce your invariants!
checkArgument(!suitsIter.hasNext(),
"Unexpected extra suits: %s", ImmutableList.copyOf(suitsIter));
checkArgument(!pilotsIter.hasNext(),
"Unexpected extra pilots: %s", ImmutableList.copyOf(pilotsIter));
return builder.build();
}
Now you don't need to maintain a complex custom Iterator implementation - just rely on one that already exists!
We can also generalize assignPilots() into a generic utility that works for any two inputs, like so:
public static List zipLists(
BiFunction factory, Iterable left, Iterable right) {
Iterator lIter = left.iterator();
Iterator rIter = right.iterator();
ImmutableList.Builder builder = ImmutableList.builder();
while (lIter.hasNext() && rIter.hasNext()) {
builder.add(factory.apply(lIter.next(), rIter.next()));
}
checkArgument(!lIter.hasNext(),
"Unexpected extra left elements: %s", ImmutableList.copyOf(lIter));
checkArgument(!rIter.hasNext(),
"Unexpected extra right elements: %s", ImmutableList.copyOf(rIter));
return builder.build();
}
Which you'd then invoke like so:
List occupiedSuits = zipLists(OccupiedSuit::new, suits, pilots);
Example code uses Guava's Preconditions and ImmutableList - if you don't use Guava it's easy enough to inline and swap to ArrayList, but just use Guava :)