Java 同时提供对两个列表内容的迭代器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3137944/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Provide an iterator over the contents of two lists simultaneously?
提问by Setsuna F. Seiei
Suppose I have this:
假设我有这个:
public class Unit<MobileSuit, Pilot> {
...
List<MobileSuit> mobileSuits;
List<Pilot> pilots;
...
}
And I would like to iterate through the pair of each in the simplest way outside of that class. How should I go about doing that? I thought about doing this:
我想以该类之外最简单的方式遍历每个对。我该怎么做呢?我想过这样做:
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);
}
}
Something along those lines.
沿着这些路线的东西。
Anyway, the problem is that I can't really return just a single object from next(), and I also can't have a Iterator take more than one type. So, any thoughts?
无论如何,问题是我不能真正从 next() 返回单个对象,而且我也不能让迭代器采用多种类型。那么,有什么想法吗?
Also, I can't make a new class to combine MobileSuit and Pilot. I need to keep them separate, even though I'm iterating through both at a time. 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. This class needs to be processed in other places, so I'd have to unify a interface around that and a lot of other stuff. Basically, assume MobileSuit and Pilot need to be separated.
此外,我无法创建一个新类来结合 MobileSuit 和 Pilot。我需要将它们分开,即使我一次遍历两者。原因是可能有没有飞行员的机动战士,我不知道如何通过将它们保持在同一班级来解决这个问题。这个类需要在其他地方处理,所以我必须围绕它和很多其他东西统一一个接口。基本上,假设需要将 MobileSuit 和 Pilot 分开。
采纳答案by Stephen C
Anyway, the problem is that I can't really return just a single object from next(), and I also can't have a Iterator take more than one type. So, any thoughts?
无论如何,问题是我不能真正从 next() 返回单个对象,而且我也不能让迭代器采用多种类型。那么,有什么想法吗?
Obviously you are going to need a light-weight "pair" class. This is roughly analogous to the Map.Entry
inner class.
显然,您将需要一个轻量级的“配对”类。这大致类似于Map.Entry
内部类。
Here's a rough cut at a generic solution:
这是通用解决方案的粗略切割:
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());
}
...
}
Note: this doesn't explicitly deal with cases where the lists have different lengths. What will happen is that extra elements at the end of the longer list will be silently ignored.
注意:这并没有明确处理列表长度不同的情况。将会发生的是较长列表末尾的额外元素将被默默忽略。
回答by Anon.
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;
}
}
回答by Jon Skeet
Also, I can't make a new class to combine MobileSuit and Pilot.
此外,我无法创建一个新类来结合 MobileSuit 和 Pilot。
That doesn't sound correct. It sounds like you can't replaceMobileSuit and Pilot by a single class, but I don't see any reason why you can't have a single class that combinesthem - i.e. one which just has a getPilot()
method and a getMobileSuit()
method. You could use a generic Pair
class for the same purpose, but a custom class would be easier to use.
这听起来不正确。听起来您不能用单个类替换MobileSuit 和 Pilot,但我看不出有任何理由不能将它们组合在一起的单个类- 即只有一个getPilot()
方法和一个getMobileSuit()
方法的类。您可以将通用Pair
类用于相同目的,但自定义类会更易于使用。
On the other hand, if you want to do this sort of "zipping" operation in multiple places, it might be one solution. Alternatively, you could write a generic interface to represent the act of combining the two distinct items - which could return a SuitedPilot
or whatever your combination class is.
另一方面,如果您想在多个地方进行这种“压缩”操作,它可能是一种解决方案。或者,您可以编写一个通用接口来表示组合两个不同项目的行为 - 它可以返回 aSuitedPilot
或您的组合类是什么。
回答by Newtopian
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.
为什么不将 MannedMobileSuit 类作为包含飞行员实例的 MobileSuit 的子类?这将通过使用 getPilot 方法来解决您的问题。
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
通常当你遇到这样的问题(需要返回两个实例)是因为你的对象模型不合适,应该改变。保持你的选择
回答by ColinD
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()
.
您可以只使用 a Map<MobileSuit, Pilot>
,其中null
映射到 a的值MobileSuit
表示没有飞行员。的Iterator
可能仅仅是一个Iterator<Map.Entry<MobileSuit, Pilot>>
通过检索map.entrySet().iterator()
。
回答by Aymen
Isn't that enough ?
这还不够吗?
for(MobileSuit ms : MobileSuits) {
for(Pilot p : pilots){
//TODO
}
}
回答by dhardy
This is copied+edited from Stephen C's answer. Feel free to use:
这是从斯蒂芬 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();
...
}
}
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.
一旦任一列表的元素不足,这就会停止,因此您可能希望在创建 IterablePair 之前检查列表的大小是否相同。
回答by user2224844
for(int i=0; i < mobileSuits.size(); i++) {
MobileSuit suit = mobileSuits.get(i);
Pilot pilot = pilots.get(i);
...
}
回答by burythehammer
Came across this page trying to solve this issue, and turns out that there's a library out there that's already solved it using Java 8 streams (check out the Zip function).
遇到这个试图解决这个问题的页面,结果发现有一个库已经使用 Java 8 流解决了这个问题(查看 Zip 函数)。
You can convert a list to a stream just by calling list.stream()
您可以通过调用将列表转换为流 list.stream()
https://github.com/poetix/protonpack
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"));
回答by dimo414
Basically, assume MobileSuit and Pilot need to be separated.
基本上,假设需要将 MobileSuit 和 Pilot 分开。
That's fine, but hereyou'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.:
这很好,但在这里您试图将它们视为一个单元,因此请以这种方式构建您的代码。上面的建议使用一个Pair
类 or Map.Entry
,但最好提供一个明确命名的对象来表示 aMobileSuit
和 a 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
}
Then, rather than constructing a custom Iterator
/Iterable
, just write a helper function that zips up the two lists. For example:
然后,而不是构建自定义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();
}
Now you don't need to maintain a complex custom Iterator
implementation - just rely on one that already exists!
现在您不需要维护一个复杂的自定义Iterator
实现——只需依赖一个已经存在的实现!
We can also generalize assignPilots()
into a generic utility that works for any two inputs, like so:
我们还可以概括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();
}
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 :)
示例代码使用番石榴的Preconditions
和ImmutableList
-如果你不使用它番石榴很容易够到内联和互换ArrayList
,但只使用番石榴:)