如何最优雅地迭代并行集合?

时间:2009-09-02 04:14:03

标签: java collections

假设我有2个并行集合,例如:List<String>中的人名列表和List<Int>中相同顺序的年龄列表(以便每个集合中的任何给定索引)是指同一个人)。

我想同时遍历这两个集合并获取每个人的姓名和年龄并使用它做一些事情。使用数组可以轻松完成:

for (int i = 0; i < names.length; i++) {
   do something with names[i] ....
   do something with ages[i].....
}

使用集合执行此操作时,最优雅的方式(就可读性和速度而言)是什么?

8 个答案:

答案 0 :(得分:60)

it1 = coll1.iterator();
it2 = coll2.iterator();
while(it1.hasNext() && it2.hasNext()) {
   value1 = it1.next();
   value2 = it2.next();
   do something with it1 and it2;
}

当较短的集合耗尽时,此版本终止;或者,您可以继续,直到较长的一个用尽,设置值1。 value2为null。

答案 1 :(得分:33)

我会创建一个封装两者的新对象。把它扔进数组并迭代它。

List<Person>

其中

public class Person {
    public string name;
    public int age;
}

答案 2 :(得分:10)

您可以为它创建一个界面:

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

然后你有:

Collection<String> c1 = ...
Collection<Long> c2 = ...
zip(c1, c2, new ZipIterator<String, Long>() {
  public boolean each(String s, Long l) {
    ...
  }
});

答案 3 :(得分:6)

for (int i = 0; i < names.length; ++i) {
  name = names.get(i);
  age = ages.get(i);
  // do your stuff
}

这并不重要。你的代码不会得到优雅的分数。只要这样做就可以了。请不要臃肿。

答案 4 :(得分:3)

我接受了@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());
    }
}

用法:

zip(list1, list2, (v1, v2) -> {
    // Do stuff
});

答案 5 :(得分:1)

虽然提交的解决方案是正确的,但我更喜欢以下方法,因为它遵循有效的java项目57的指南:最小化局部变量的范围:

    for (Iterator<String> i = lst1.iterator(), ii = lst2.iterator(); i.hasNext() && ii.hasNext(); ) {
        String e1 = i.next();
        String e2 = ii.next();
        ....
    }

答案 6 :(得分:0)

正如jeef3所建议的那样,建模真正的域而不是保持单独的,隐式耦合的列表是正确的方法......当这是一个选项。

您可能无法采用此方法的原因有多种。如果是的话......

一个。您可以使用回调方法,如cletus所示。

B中。您仍然可以选择公开为每个复合实例公开域对象元素的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();
    }
  };
}

℃。您可以使用此变体并使用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);
}

请注意,B方法也可以通过引入域接口支持列表更新,并让迭代器返回“实时”对象。

答案 7 :(得分:0)

我刚刚在这个similar question中发布了这个函数(@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();
}

然后,您可以为BiFunction提供工厂操作,例如值类型的构造函数:

List<Person> people = zipLists(Person::new, names, ages);

如果真的只是想迭代它们并进行一些操作,而不是构建一个新的集合,你可以将BiFunction换成BiConsumer并拥有函数返回void