Java - 如何在列表中迭代每两个元素?

9

如何在处理两个元素的同时遍历一个列表?

示例:

List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
for(int i = 0; i < strings.size(); i++){
    String first = strings.get(i);
    String second = null;
    if(strings.size() > i + 1){
        second = strings.get(i + 1);
    }
    System.out.println("First [" + first + "] - Second [" + second + "]");
}

结果:

First [item 1] - Second [item 2]
First [item 2] - Second [item 3]
First [item 3] - Second [item 4]
First [item 4] - Second [null]

我希望实现以下目标:

First [item 1] - Second [item 2]
First [item 3] - Second [item 4]
11个回答

20
只需在每次迭代中将i增加2,而不是使用++(它会递增1):
for (int i = 0; i + 1 < strings.size(); i += 2) {
  String first = strings.get(i);
  String second = strings.get(i + 1);
}

3
这个能不能超出界限? - CQM
如果列表的大小恰好为2怎么办?循环不会遍历这两个元素! - DoubleS
1
@IdoSarShalom 哈哈,经典的偏移一。感谢您的注意。现在希望已经修复了。 - hyde

11

我使用 Java8 BiConsumer 创建了以下方法:

public static <T> void tupleIterator(Iterable<T> iterable, BiConsumer<T, T> consumer) {
    Iterator<T> it = iterable.iterator();
    if(!it.hasNext()) return;
    T first = it.next();

    while(it.hasNext()) {
        T next = it.next();
        consumer.accept(first, next);
        first = next;
    }
}

使用方法如下:

List<String> myIterable = Arrays.asList("1", "2", "3");
tupleIterator(myIterable, (obj1, obj2) -> {
    System.out.println(obj1 + " " + obj2);
});

这将输出:
1 2
2 3

7
你需要修改并递增第二个值的i,修改语句如下:
second = strings.get(i + 1);

to

second = strings.get(++i);

这将增加i的值,因为这似乎是所期望的行为。
因此,您的代码将是:
```` code would be: ````
List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
for(int i = 0; i < strings.size(); i++){
    String first = strings.get(i);
    String second = null;
    if(strings.size() > i + 1){
        second = strings.get(++i); //Change here
    }
    System.out.println("First [" + first + "] - Second [" + second + "]");
}

我只会在for循环语句的第三部分不断修改索引变量。循环内多个地方增加它会导致代码不够清晰(即更难理解和维护,更容易出现错误)。这是for循环常见的惯用法 / 经验法则,如果按照自己的方式编写,可能使用while循环会更好。 - hyde
2
@hyde,我考虑了一下,但后来决定更改最小值,为了让OP理解,我同意只在一个地方修改for循环计数器,因为这样可以提供更好的理解和可读性。 - Habib
谢谢大家,两个答案都非常有帮助! - stikkos

4
List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");    
int i = 0;  
for(; i < strings.size() - 1; i+=2){  
    String first = strings.get(i);  
    String second =  strings.get(i + 1);  
    System.out.println("First [" + first + "] - Second [" + second + "]");  
}  
//For odd sized lists
if(i < strings.size()){         
    System.out.println("First [" + strings.get(i) + "]");  
}

你把 int i = 0 放在循环外面有什么原因吗? - Marco Forberg
@MarcoForberg:是的。为了在for循环之外检查奇数大小的列表。 - Cratylus
嗯,好的,但为什么不像往常一样检查奇数大小:“strings.size() % 2”呢?嗯...如果我错了,请纠正我,但在循环执行后,我认为它将比列表的大小大一个。 - Marco Forberg
@MarcoForberg:你也可以这样做。 - Cratylus
你忘记在 if 块中给 first 赋一个新值了。 - Marco Forberg

2
当然,我们应该提供一个通用的解决方案;-)
public static void main(String[] args) {
    List<Integer> list = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
    for (Pair<Integer> p : Pair.over(list)) {
        System.out.printf("%d, %d\n", p.first, p.second);
    }
}

static class Pair<T> {
    T first;

    T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public static <T> Iterable<Pair<T>> over(Collection<T> collection) {
        return new PairWise<T>(collection);
    }

    private static class PairWise<T> implements Iterable<Pair<T>>, Iterator<Pair<T>> {

        final Iterator<T> iterator;

        PairWise(Collection<T> collection) {
            super();
            this.iterator = collection.iterator();
        }

        @Override
        public Iterator<Pair<T>> iterator() {
            return this;
        }

        @Override
        public boolean hasNext() {
            return iterator.hasNext();
        }

        @Override
        public Pair<T> next() {
            T first = null;
            T second = null;
            if (iterator.hasNext())
                first = iterator.next();
            else
                throw new NoSuchElementException();
            if (iterator.hasNext())
                second = iterator.next();
            return new Pair<T>(first, second);
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }

    }
}

2
如果在每次迭代中将i增加2,会怎样呢?应该这样做...否则考虑在实际循环内增加i。

1
你可以使用迭代器来避免索引,这适用于任何可迭代对象,而不仅仅是列表。只需获取一个迭代器,并在每个循环迭代中将其增加两次即可。
List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
Iterator<String> stringsIterator = strings.iterator();
while (stringsIterator.hasNext()) {
  String first = stringsIterator.next();
  String second = stringsIterator.next();
  System.out.println("First [" + first + "] - Second [" + second + "]");
}

假设列表长度为偶数,在最后一次遍历时,如果长度为奇数,则会抛出NoSuchElementException异常。你可以用以下方法处理这个问题:
  • 使用try-catch语句;
  • 在操作前加入守卫条件检查长度是否为偶数;
  • 在获取第二个元素之前进行检查。

检查第二个元素:

List<String> strings = Arrays.asList("item 1", "item 2", "item 3");
Iterator<String> stringsIterator = strings.iterator();
while (stringsIterator.hasNext()) {
  String first = stringsIterator.next();
  String second = stringIterator.hasNext() ? stringIterator.next() : null;
  System.out.println("First [" + first + "] - Second [" + second + "]");
}

迭代器会让一些人感到困惑,因此您也可以使用带有分支和辅助翻转变量的for-each循环来实现奇偶性。这样做要糟糕得多,因为它使循环的逻辑更加复杂,以简化迭代:而不是在每次通过循环时顺序且无需分支地执行操作,您必须进行两次操作并进行精神分支。注意,如果最后一个元素长度为奇数,则会跳过它;如果想要处理这些情况,可以在之后添加isFirst的检查。
List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
boolean isFirst = true;
String first = null;
String second = null;
for (String string : strings) {
  if (isFirst) {
    first = string;
    isFirst = false;
  } else {
    second = string;
    isFirst = true;
    System.out.println("First [" + first + "] - Second [" + second + "]");
  }
}

最后,注意所有这些迭代器和辅助变量都有过度的范围(它们仅对循环本身有用,因此会污染局部环境):它们可以被包装在块中以限制范围,尽管通常结果嵌套被认为比过度范围更糟糕。
List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
{
  Iterator<String> stringsIterator = strings.iterator();
  while (stringsIterator.hasNext()) {
    String first = stringsIterator.next();
    String second = stringsIterator.next();
    System.out.println("First [" + first + "] - Second [" + second + "]");
  }
}

这是一个基本问题,但是没有索引的干净答案有点微妙。当你有一个扁平化的对列表时(就像 [a0, b0, a1, b1, ...]),这个问题在现实生活中会出现(也是我来到这里的原因)。 - Nils von Barth

1

0
for(int i = 0; i < strings.size(); i++){
    String first = strings.get(i++);
    String second = null;
    if(strings.size() > i){
        second = strings.get(i);
    }
    System.out.println("First [" + first + "] - Second [" + second + "]");
}

0
List<String> strings = Arrays.asList("item 1", "item 2", "item 3", "item 4");
for(int i = 0; i < strings.size(); i++){
    if(i½2 = 0){
        String first = strings.get(i);
        System.out.print("First [" + first + "] ");
    }else{
         String second = strings.get(i + 1);
         System.out.println("- Second [" + second + "]");
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接