Java 如何最优雅地遍历并行集合?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1365793/
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
How to most elegantly iterate through parallel collections?
提问by tekumara
Say I have 2 parallel collections, eg: a list of people's names in a List<String>
and a list of their age in a List<Int>
in the same order (so that any given index in each collection refers to the same person).
假设我有 2 个并行集合,例如:a 中的人名List<String>
列表和 a 中的年龄列表,List<Int>
按相同顺序排列(以便每个集合中的任何给定索引都指向同一个人)。
I want to iterate through both collections at the same time and fetch the name and age of each person and do something with it. With arrays this is easily done with:
我想同时遍历两个集合并获取每个人的姓名和年龄并对其进行处理。使用数组,这很容易完成:
for (int i = 0; i < names.length; i++) {
do something with names[i] ....
do something with ages[i].....
}
What would be the most elegant way (in terms of readability and speed) of doing this with collections?
使用集合执行此操作的最优雅方式(在可读性和速度方面)是什么?
采纳答案by jeef3
I would create a new object that encapsulates the two. Throw that in the array and iterate over that.
我将创建一个封装这两者的新对象。将其放入数组中并对其进行迭代。
List<Person>
Where
在哪里
public class Person {
public string name;
public int age;
}
回答by Martin v. L?wis
it1 = coll1.iterator();
it2 = coll2.iterator();
while(it1.hasNext() && it2.hasNext()) {
value1 = it1.next();
value2 = it2.next();
do something with it1 and it2;
}
This version terminates when the shorter collection is exhausted; alternatively, you could continue until the longer one is exhausted, setting value1 resp. value2 to null.
当较短的集合耗尽时,此版本终止;或者,您可以继续,直到用完更长的时间,分别设置 value1。value2 为空。
回答by André Chalella
for (int i = 0; i < names.length; ++i) {
name = names.get(i);
age = ages.get(i);
// do your stuff
}
It doesn't really matter. Your code won't get points for elegance. Just do it so that it works. And please don't bloat.
这并不重要。你的代码不会因为优雅而获得分数。只要这样做,它就可以工作。并且请不要膨胀。
回答by cletus
You could create an interface for it:
你可以为它创建一个接口:
public interface ZipIterator<T,U> {
boolean each(T t, U u);
}
public class ZipUtils {
public static <T,U> boolean zip(Collection<T> ct, Collection<U> cu, ZipIterator<T,U> each) {
Iterator<T> it = ct.iterator();
Iterator<U> iu = cu.iterator();
while (it.hasNext() && iu.hasNext()) {
if (!each.each(it.next(), iu.next()) {
return false;
}
}
return !it.hasNext() && !iu.hasNext();
}
}
And then you have:
然后你有:
Collection<String> c1 = ...
Collection<Long> c2 = ...
zip(c1, c2, new ZipIterator<String, Long>() {
public boolean each(String s, Long l) {
...
}
});
回答by Dilum Ranatunga
As suggested by jeef3, modeling the true domain rather than keeping separate, implicitly coupled Lists is the right way to go... when this is an option.
正如 jeef3 所建议的那样,对真正的域进行建模而不是保持单独的、隐式耦合的列表是正确的方法……当这是一个选项时。
There are various reasons why you might not be able to adopt this approach. If so...
您可能无法采用此方法的原因有多种。如果是这样的话...
A. You can use a callback approach, as suggested by cletus.
答:您可以按照 cletus 的建议使用回调方法。
B. You can still choose to expose an Iterator that exposes domain object element for each composite instance. This approach doesn't force you to keep a parallel List structure around.
B. 您仍然可以选择公开一个 Iterator,该 Iterator 为每个复合实例公开域对象元素。这种方法不会强迫您保持并行的 List 结构。
private List<String> _names = ...;
private List<Integer> _ages = ...;
Iterator<Person> allPeople() {
final Iterator<String> ni = _names.iterator();
final Iterator<Integer> ai = _ages.iterator();
return new Iterator() {
public boolean hasNext() {
return ni.hasNext();
}
public Person next() {
return new Person(ni.next(), ai.next());
}
public void remove() {
ni.remove();
ai.remove();
}
};
}
C. You can use a variation of this and use a RowSet style cursor API. Let's say IPerson
is an interface that describes Person. Then we can do:
C. 您可以使用此变体并使用 RowSet 样式的游标 API。假设IPerson
是一个描述 Person 的接口。然后我们可以这样做:
public interface IPerson {
String getName();
void setName(String name);
...
}
public interface ICursor<T> {
boolean next();
T current();
}
private static class PersonCursor implements IPerson, ICursor<IPerson> {
private final List<String> _names;
...
private int _index = -1;
PersonCursor(List<String> names, List<Integer> ages) {
_names = names;
...
}
public boolean next() {
return ++_index < _names.size();
}
public Person current() {
return this;
}
public String getName() {
return _names.get(_index);
}
public void setName(String name) {
_names.set(0, name);
}
...
}
private List<String> _names = ...;
private List<Integer> _ages = ...;
Cursor<Person> allPeople() {
return new PersonCursor(_names, _ages);
}
Note that the B approach also be made to support updates to list by introducing a Domain interface, and having the Iterator return 'live' objects.
请注意,B 方法还通过引入域接口并让迭代器返回“活动”对象来支持对列表的更新。
回答by dimo414
I just posted this function in this similar question(which @Nils von Barth asserts is not a duplicate ;) ), but it's equally applicable here:
我刚刚在这个类似的问题中发布了这个函数(@Nils von Barth 断言不是重复的 ;) ),但它在这里同样适用:
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()));
}
// 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(!lIter.hasNext(),
"Unexpected extra left elements: %s", ImmutableList.copyOf(lIter));
checkArgument(!rIter.hasNext(),
"Unexpected extra right elements: %s", ImmutableList.copyOf(rIter));
return builder.build();
}
You can then provide a factory operation for the BiFunction
, such as a value-type's constructor:
然后,您可以为 提供工厂操作BiFunction
,例如值类型的构造函数:
List<Person> people = zipLists(Person::new, names, ages);
If you reallyjust want to iterate over them and do some operation, rather than construct a new collection, you could swap the BiFunction
for a BiConsumer
and have the function return void
.
如果你真的只想迭代它们并做一些操作,而不是构造一个新的集合,你可以交换BiFunction
for aBiConsumer
并让函数 return void
。
回答by StationaryTraveller
I took @cletus comment and Improved it abit, And that's what I use:
我接受了@cletus 评论并改进了它,这就是我使用的:
public static <T,U> void zip(Collection<T> ct, Collection<U> cu, BiConsumer<T, U> consumer) {
Iterator<T> it = ct.iterator();
Iterator<U> iu = cu.iterator();
while (it.hasNext() && iu.hasNext()) {
consumer.accept(it.next(), iu.next());
}
}
Usage:
用法:
zip(list1, list2, (v1, v2) -> {
// Do stuff
});
回答by user1121883
While the submitted solutions are correct I prefer the following one because it follows the guides from effective java item 57: minimize the scope of local variables:
虽然提交的解决方案是正确的,但我更喜欢以下解决方案,因为它遵循了有效 java 项目 57 中的指南:最小化局部变量的范围:
for (Iterator<String> i = lst1.iterator(), ii = lst2.iterator(); i.hasNext() && ii.hasNext(); ) {
String e1 = i.next();
String e2 = ii.next();
....
}