同时提供两个列表内容的迭代器?

时间:2010-06-29 04:53:57

标签: java generics iterator

假设我有这个:

public class Unit<MobileSuit, Pilot> {

    ...

    List<MobileSuit> mobileSuits;
    List<Pilot> pilots;

    ...
}

我想以最简单的方式在该类之外迭代这对中的每一对。我应该怎么做呢?我想过这样做:

public class Unit<MobileSuit, Pilot> {

    ...
    Iterator<MobileSuit> iteratinMechas;
    Iterator<Pilot> iteratinPeople;

    class IteratorCustom<MobileSuit, Pilot> implements Iterator {

        public boolean hasNext() {
            return iteratinMechas.hasNext() && iteratinPeople.hasNext();
        }

        public void remove() {
            iteratinMechas.remove();
            iteratinPeople.remove();
        }

        public Object next() {
            // /!\
        }

    }

    public Iterator iterator() {
        return new IteratorCustom<MobileSuit, Pilot>(mobileSuits, pilots);
    }
}

这些方面的东西。

无论如何,问题是我不能真正从next()返回一个对象,而且我也不能让Iterator占用多个类型。那么,有什么想法吗?

另外,我不能组建一个新的课程来结合MobileSuit和Pilot。我需要将它们分开,即使我一次迭代它们。原因是可能有没有飞行员的移动套装,我不确定如何通过将它们保持在同一级别来解决这个问题。这个类需要在其他地方进行处理,所以我必须统一一个界面和很多其他东西。基本上,假设MobileSuit和Pilot需要分开。

11 个答案:

答案 0 :(得分:12)

  

无论如何,问题是我不能真正从next()返回一个对象,而且我也不能让Iterator占用多个类型。那么,有什么想法吗?

显然你需要一个轻量级的“配对”课程。这大致类似于Map.Entry内部类。

以下是通用解决方案的粗略内容:

public class ParallelIterator <T1, T2> implements Iterator<Pair<T1, T2>> {

    public class Pair<TT1, TT2> {
        private final TT1 v1;
        private final TT2 v2;
        private Pair(TT1 v1, TT2 v2) { this.v1 = v1; this.v2 = v2; }
        ...
    }

    private final Iterator<T1> it1;
    private final Iterator<T2> it2;

    public ParallelIterator(Iterator<T1> it1, Iterator<T2> it2) { 
        this.it1 = it1; this.it2 = it2;
    }

    public boolean hasNext() { return it1.hasNext() && it2.hasNext(); }

    public Pair<T1, T2> next() {
        return new Pair<T1, T2>(it1.next(), it2.next());
    }

    ...

}

注意:这并未明确处理列表长度不同的情况。将会发生的事情是,较长列表末尾的额外元素将被忽略。

答案 1 :(得分:5)

这是从Stephen C的回答中复制+编辑的。随意使用:

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();
        ...
    }
}

只要列表中的元素不存在,就会停止,因此您可能希望在创建IterablePair之前检查列表大小相等。

答案 2 :(得分:4)

  

另外,我不能组建一个新的课程来结合MobileSuit和Pilot。

这听起来不正确。听起来你不能通过一个类替换 MobileSuit和Pilot,但是我没有看到为什么你不能拥有结合它们的单个类的任何理由 - 即只有getPilot()方法和getMobileSuit()方法的方法。您可以将通用Pair类用于相同目的,但自定义类更易于使用。

另一方面,如果你想在多个地方进行这种“压缩”操作,它可能是一个解决方案。或者,您可以编写一个通用接口来表示组合两个不同项的行为 - 这可能会返回SuitedPilot或任何组合类。

答案 3 :(得分:2)

  

原因是可能存在没有飞行员的移动套装,而且我不确定如何通过将它们保持在同一级别来解决这个问题。

你可以使用空值,对吗?这是正确的做法 - 让每件套装跟踪其飞行员。如果它没有导频,那么在那里用空值表示。

但是,如果你因为某些原因而没有做到这一点......

public class SuitAndPilot
{
    public MobileSuit suit;
    public Pilot pilot;

    public SuitAndPilot(Suit s, Pilot p) {
           suit = s;
           pilot = p;
    }
}

答案 4 :(得分:1)

为什么不将MannedMobileSuit类作为包含试用实例的MobileSuit的子类?这将通过使用getPilot方法解决您的问题。

通常当您遇到此类问题(需要返回两个实例)时,这是因为您的对象模型不合适且应该更改。保持选项开放

答案 5 :(得分:1)

for(int i=0; i < mobileSuits.size(); i++) {
  MobileSuit suit = mobileSuits.get(i);
  Pilot pilot = pilots.get(i);
  ...
}

答案 6 :(得分:1)

  

基本上,假设MobileSuit和Pilot需要分开。

没关系,但在这里你试图将它们视为一个单元,因此以这种方式构建代码。上面的建议使用了Pair类或Map.Entry,但提供一个代表MobileSuitPilot的明确命名的对象要好得多,例如:

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
}

然后,不要构建自定义Iterator / Iterable,而只需编写zips up the two lists的辅助函数。例如:

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();
}

现在您不需要维护复杂的自定义Iterator实施 - 只需依赖已经存在的实现!

我们还可以将assignPilots()推广到适用于任何两个输入的通用实用程序中,如下所示:

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();
}

然后你会这样调用:

List<OccupiedSuit> occupiedSuits = zipLists(OccupiedSuit::new, suits, pilots);

示例代码使用Guava&#39; PreconditionsImmutableList - 如果您不使用Guava,它很容易内联和交换到ArrayList,但只使用Guava:)

答案 7 :(得分:0)

您可以使用Map<MobileSuit, Pilot>,其中映射到null的{​​{1}}值表示没有导频。 MobileSuit可能只是由Iterator检索的Iterator<Map.Entry<MobileSuit, Pilot>>

答案 8 :(得分:0)

试图解决这个问题,并且发现那里有一个库,已经使用Java 8流解决了它(查看Zip函数)。

您只需拨打list.stream()

即可将列表转换为流

https://github.com/poetix/protonpack

Stream<String> streamA = Stream.of("A", "B", "C");
Stream<String> streamB  = Stream.of("Apple", "Banana", "Carrot", "Doughnut");
List<String> zipped = StreamUtils.zip(streamA,
                                      streamB,
                                      (a, b) -> a + " is for " + b)
                                 .collect(Collectors.toList());

assertThat(zipped,
           contains("A is for Apple", "B is for Banana", "C is for Carrot"));

答案 9 :(得分:0)

通过user2224844改善答案,这是一个简单的版本,将尝试不运行以免发生异常:

final Iterator<String> pilotIterator = pilots.iterator();
            mobileSuits.forEach(m -> {
                    Pilot p = pilotIterator.hasNext()? pilotIterator.next():nullOrWahtever;
<Now do your work with m and p variables>
    ...
    });

答案 10 :(得分:-3)

还不够吗?

for(MobileSuit ms : MobileSuits) {
    for(Pilot p : pilots){
        //TODO
    }
}