Provide an iterator over the contents of two lists simultaneously?

前端 未结 11 1949
逝去的感伤
逝去的感伤 2020-12-03 04:45

Suppose I have this:

public class Unit {

    ...

    List mobileSuits;
    List pilots;

    ...
}
         


        
相关标签:
11条回答
  • 2020-12-03 05:13

    This is copied+edited from Stephen C's answer. Feel free to use:

    public class Pair<T1, T2> {
        private final T1 v1;
        private final T2 v2;
        Pair(T1 v1, T2 v2) {
            this.v1 = v1;
            this.v2 = v2;
        }
        public T1 first(){
            return v1;
        }
        public T2 second(){
            return v2;
        }
    }
    
    public class ParallelIterator <T1, T2> implements Iterator<Pair<T1, T2>> {
    
        private final Iterator<T1> it1;
        private final Iterator<T2> it2;
    
        public ParallelIterator(Iterator<T1> it1, Iterator<T2> it2) { 
            this.it1 = it1; this.it2 = it2;
        }
    
        @Override
        public boolean hasNext() { return it1.hasNext() && it2.hasNext(); }
    
        @Override
        public Pair<T1, T2> next() {
            return new Pair<T1, T2>(it1.next(), it2.next());
        }
    
        @Override
        public void remove(){
            it1.remove();
            it2.remove();
        }
    }
    
    public class IterablePair <T1, T2> implements Iterable<Pair<T1,T2>> {
        private final List<T1> first;
        private final List<T2> second;
    
        public IterablePair(List<T1> first, List<T2> second) { 
            this.first = first;
            this.second = second;
        }
    
        @Override
        public Iterator<Pair<T1, T2>> iterator(){
            return new ParallelIterator<T1,T2>( first.iterator(), second.iterator() );
        }
    }
    
    void someFunction(){
        IterablePair<X,Y> listPair = new IterablePair<X,Y>( x, y );
        for( Pair<X,Y> pair : listPair ){
            X x = pair.first();
            ...
        }
    }
    

    This stops as soon as either list is out of elements, so you might want to check lists have equal size before creating an IterablePair.

    0 讨论(0)
  • 2020-12-03 05:21

    Why not have a class MannedMobileSuit as a subclass of MobileSuit that contains an instance of a pilot ? That would solve your problem by having a getPilot method.

    Usually when you get such problems (needing to return two instances) it is because your Object model is not appropriate and should be changed. Keep your options open

    0 讨论(0)
  • 2020-12-03 05:27

    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<OccupiedSuit> assignPilots(
        Iterable<MobileSuit> suits, Iterable<Pilot> pilots) {
      Iterator<MobileSuit> suitsIter = suits.iterator();
      Iterator<Pilot> pilotsIter = pilots.iterator();
      ImmutableList.Builder<OccupiedSuit> 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 <L,R,M> List<M> zipLists(
        BiFunction<L,R,M> factory, Iterable<L> left, Iterable<R> right) {
      Iterator<L> lIter = left.iterator();
      Iterator<R> rIter = right.iterator();
      ImmutableList.Builder<M> 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<OccupiedSuit> 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 :)

    0 讨论(0)
  • 2020-12-03 05:27
    for(int i=0; i < mobileSuits.size(); i++) {
      MobileSuit suit = mobileSuits.get(i);
      Pilot pilot = pilots.get(i);
      ...
    }
    
    0 讨论(0)
  • 2020-12-03 05:28

    The reason is that there might be Mobile Suits that have no pilots, and I'm not sure how to fix that by keeping them at the same class.

    You can use null values, right? Which is the correct way of doing it - have each suit keep track of its pilot. If it has no pilot, then indicate that with a null value there.

    But, if you're dead set on not doing that for some reason...

    public class SuitAndPilot
    {
        public MobileSuit suit;
        public Pilot pilot;
    
        public SuitAndPilot(Suit s, Pilot p) {
               suit = s;
               pilot = p;
        }
    }
    
    0 讨论(0)
  • 2020-12-03 05:29

    You could just use a Map<MobileSuit, Pilot>, where a null value mapped to a MobileSuit indicates no pilot. The Iterator could just be an Iterator<Map.Entry<MobileSuit, Pilot>> retrieved by map.entrySet().iterator().

    0 讨论(0)
提交回复
热议问题