Подтвердить что ты не робот

Обеспечить итератор над содержимым двух списков одновременно?

Предположим, что у меня есть это:

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 необходимо разделить.

4b9b3361

Ответ 1

Во всяком случае, проблема в том, что я не могу реально вернуть только один объект из 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());
    }

    ...

}

Примечание. Это явно не касается случаев, когда списки имеют разную длину. Что произойдет, так это то, что дополнительные элементы в конце более длинного списка будут игнорироваться.

Ответ 2

Это скопировано + отредактировано с ответа Стивена 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.

Ответ 3

Кроме того, я не могу создать новый класс для объединения MobileSuit и Pilot.

Это звучит неправильно. Похоже, вы не можете заменить MobileSuit и Pilot одним классом, но я не вижу причин, по которым вы не можете использовать один класс, который их объединяет, то есть тот, у которого есть только метод getPilot() и getMobileSuit(). Вы можете использовать общий класс Pair для той же цели, но пользовательский класс будет проще использовать.

С другой стороны, если вы хотите выполнить эту операцию "запирания" в нескольких местах, это может быть одно решение. В качестве альтернативы вы могли бы написать общий интерфейс для представления акта объединения двух разных элементов - который мог бы вернуть SuitedPilot или любой другой класс вашей комбинации.

Ответ 4

Причина в том, что могут быть мобильные костюмы, у которых нет пилотов, и я не уверен, как это исправить, удерживая их в одном классе.

Вы можете использовать нулевые значения, правильно? Каков правильный способ сделать это - каждый костюм отслеживает свой пилот. Если у него нет пилот-сигнала, укажите, что с нулевым значением.

Но, если вы мертвы, когда не делаете этого по какой-то причине...

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

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

Ответ 5

Почему бы не создать класс MannedMobileSuit в качестве подкласса MobileSuit, который содержит экземпляр пилота? Это поможет решить вашу проблему с помощью метода getPilot.

Обычно, когда вы получаете такие проблемы (нужно вернуть два экземпляра), это потому, что ваша модель объекта не подходит и должна быть изменена. Сохраняйте свои возможности

Ответ 6

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

Ответ 7

В принципе, предположим, что нужно отключить MobileSuit и Pilot.

Это хорошо, но здесь вы пытаетесь рассматривать их как единицу, поэтому структурируйте свой код таким образом. В приведенных выше предложениях используется класс Pair или Map.Entry, но гораздо лучше предоставить четко обозначенный объект, который представляет MobileSuit с Pilot, например:

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, просто напишите вспомогательную функцию, которая застегивает два списка. Например:

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 Preconditions и ImmutableList - если вы не используете Guava достаточно легко для inline и swap до ArrayList, но просто используйте Guava:)

Ответ 8

Вы можете просто использовать Map<MobileSuit, Pilot>, где значение null, отображаемое на MobileSuit, не указывает на отсутствие пилот-сигнала. Iterator может быть просто Iterator<Map.Entry<MobileSuit, Pilot>>, полученным map.entrySet().iterator().

Ответ 9

На этой странице попытался решить эту проблему, и выяснилось, что там есть библиотека, которая уже разрешила ее с использованием потоков 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"));

Ответ 10

Улучшение ответа от пользователя 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>
    ...
    });

Ответ 11

Разве этого недостаточно?

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