假设我有这个:
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需要分开。
答案 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
,但提供一个代表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
,而只需编写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; Preconditions
和ImmutableList
- 如果您不使用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
}
}