使用foreach在Java中迭代并行数组的漂亮方法

Tra*_*ebb 32 java iteration foreach

我继承了一大堆代码,它们广泛使用并行数组来存储键/值对.实际上这样做是有意义的,但是编写循环遍历这些值有点尴尬.我真的很喜欢新的Java foreach构造,但似乎没有办法使用它来迭代并行列表.

通过正常for循环,我可以轻松完成:

for (int i = 0; i < list1.length; ++i) {
    doStuff(list1[i]);
    doStuff(list2[i]);
}
Run Code Online (Sandbox Code Playgroud)

但在我看来,这不是语义上的纯粹,因为我们没有检查list2迭代期间的界限.是否有一些类似于for-each的聪明语法可用于并行列表?

Isa*_*ett 23

我会用Map自己的.但是,如果您认为一对数组在您的情况下是有意义的,那么采用两个数组并返回Iterable包装器的实用程序方法怎么样?

概念:

for (Pair<K,V> p : wrap(list1, list2)) {
    doStuff(p.getKey());
    doStuff(p.getValue());
}
Run Code Online (Sandbox Code Playgroud)

Iterable<Pair<K,V>>包装将隐藏边界检查.


Tik*_*vis 11

从增强型for循环的官方Oracle页面:

最后,它不适用于必须并行迭代多个集合的循环.这些缺点是设计师所熟知的,他们有意识地决定采用干净,简单的结构来覆盖绝大多数情况.

基本上,你最好使用正常的for循环.

如果你使用这些数组对来模拟Map,你总是可以编写一个用两个数组实现Map接口的类; 这可以让你抽象掉大部分循环.

在不查看代码的情况下,我无法告诉您此选项是否是最佳前进方式,但您可以考虑这一点.


Sea*_*son 9

这是一个有趣的练习.我创建了一个名为ParallelList的对象,它接受可变数量的类型化列表,并且可以遍历每个索引处的值(作为值列表返回):

public class ParallelList<T> implements Iterable<List<T>> {

    private final List<List<T>> lists;

    public ParallelList(List<T>... lists) {
        this.lists = new ArrayList<List<T>>(lists.length);
        this.lists.addAll(Arrays.asList(lists));
    }

    public Iterator<List<T>> iterator() {
        return new Iterator<List<T>>() {
            private int loc = 0;

            public boolean hasNext() {
                boolean hasNext = false;
                for (List<T> list : lists) {
                    hasNext |= (loc < list.size());
                }
                return hasNext;
            }

            public List<T> next() {
                List<T> vals = new ArrayList<T>(lists.size());
                for (int i=0; i<lists.size(); i++) {
                    vals.add(loc < lists.get(i).size() ? lists.get(i).get(loc) : null);
                }
                loc++;
                return vals;
            }

            public void remove() {
                for (List<T> list : lists) {
                    if (loc < list.size()) {
                        list.remove(loc);
                    }
                }
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

List<Integer> list1 = Arrays.asList(new Integer[] {1, 2, 3, 4, 5});
List<Integer> list2 = Arrays.asList(new Integer[] {6, 7, 8});
ParallelList<Integer> list = new ParallelList<Integer>(list1, list2);
for (List<Integer> ints : list) {
    System.out.println(String.format("%s, %s", ints.get(0), ints.get(1)));
}
Run Code Online (Sandbox Code Playgroud)

哪个会打印出来:

1, 6
2, 7
3, 8
4, null
5, null
Run Code Online (Sandbox Code Playgroud)

此对象支持可变长度列表,但显然可以将其修改为更严格.

不幸的是我无法摆脱ParallelList构造函数上的一个编译器警告:A generic array of List<Integer> is created for varargs parameters,所以如果有人知道如何摆脱它,让我知道:)

  • `@ SuppressWarnings` ;-) (6认同)

Ale*_*lue 6

您可以在for循环中使用第二个约束:

    for (int i = 0; i < list1.length && i < list2.length; ++i) 
    {
      doStuff(list1[i]);
      doStuff(list2[i]);
    }//for
Run Code Online (Sandbox Code Playgroud)

我遍历集合的首选方法之一是for-each循环,但正如oracle教程提到的那样,在处理并行集合时使用迭代器而不是for-each.

以下是Martinv.Löwis在一篇类似文章中的回答:

it1 = list1.iterator();
it2 = list2.iterator();
while(it1.hasNext() && it2.hasNext()) 
{
   value1 = it1.next();
   value2 = it2.next();

   doStuff(value1);
   doStuff(value2);
}//while
Run Code Online (Sandbox Code Playgroud)

迭代器的优点是它是通用的,所以如果你不知道正在使用什么集合,使用迭代器,否则如果你知道你的集合是什么,那么你就知道长度/大小函数,所以常规的for循环这里可以使用附加约束.(注意我在这篇文章中非常复数,因为一个有趣的可能性是集合使用的不同,例如一个可能是List,另一个可能是数组)

希望这有帮助.