迭代两个相关 ArrayList 的有效方法?

Efficient way to iterate over two related ArrayLists?

我正在寻找一种干净简单的方法来迭代两个直接相关的 ArrayList,因为一个的每个索引都映射到另一个的索引(在关系中)。

目前,我正在通过一个简单的 for 循环来完成它,我尝试研究 lambdas 和 for-each 循环,但没有找到将它同时应用于两个列表的方法相同大小的时间。

第一个列表:["Blue", "Red", "Green"]

第二个列表:["Sky", "Roses", "Grass"]

for(int i = 0; i < firstList.size(); i++){
    System.out.println(firstList.get(i) + " " + secondList.get(i));
}

结果:

Blue Sky
Red Roses
Green Grass

有没有办法使用 lambda 或 for-each 循环同时有效地迭代两个列表以避免使用 for 循环?

您所拥有的已经非常高效(假设您使用的是 ArrayList),简洁且可读。但是,这种情况:

I am looking for a clean and simple way to iterate over two ArrayLists that are related directly in that each index of one maps to the index of another (in relationship).

通常需要定义关系的 class,在您的情况下:

public class MyC {
    private final String color;
    private final String object;

    public MyC(String color, String object) {
        this.color = color;
        this.object = object;
    }
    public String getColor(){
        return color;
    }
    public String getObject(){
        return object;
    }

    @Override
    public String toString() {
        return "MyC{" +
                "color='" + color + '\'' +
                ", object='" + object + '\'' +
                '}';
    }
} 

那么两个列表将合二为一:

List<MyC> list = List.of(new MyC("Blue", "Sky"), new MyC("Red", "Roses"), new MyC("Green", "Grass") );

然后你可以使用:

list.forEach(System.out::println);

您可以使用“范围”语句通过 lambda 表达式迭代索引。

https://www.baeldung.com/java-stream-indices

像这样:

List<String> firstList = Arrays.asList("Blue","Red","Green");
List<String> secondList = Arrays.asList("Sky","Roses","Grass");

IntStream
    .range(0, firstList.size())
    .forEach(index -> 
        System.out.println(String.format("%s %s", firstList.get(index), secondList.get(index)))
    );

基本上是相同的方法 - 只是使用 lambda。

摆脱基于索引的访问的唯一方法是使用不同的数据结构或使用其他答案中所示的交替迭代技术。

如果您只想避免使用 for 循环,那么您可以尝试以下操作:

Iterator<String> iterator = secondList.iterator();
firstList.forEach(s -> System.out.println(s + " " + iterator.next()));

或者甚至添加到一个StringBuilder,然后在最后显示结果。

是正确的:虽然您可以循环一对数组,但您应该通过定义自己的 class 来利用 Java 作为 OOP 语言。

记录

在 Java 16 中使用新的 records 功能定义这样的 class 更加简单。当您的 class 的主要目的是透明且不变地传递数据时,将其写为记录。

一条记录默认是很简短的。编译器隐式创建构造函数、getter、equals & hashCodetoString。您只需要声明每个成员字段的类型和名称。

record ColorNoun ( String color , String noun ) {}

像常规一样使用记录 class。用 new.

实例化
ColorNoun blueSky = new ColorNoun ( "Blue" , "Sky" ) ;

请注意,记录可以在方法内局部声明,也可以像常规的那样单独声明class。

所有其他答案都是正确的,首选解决方案应该始终是@Basil Bourque 所示的解决方案,但正如其他人在评论中指出的那样,使用索引迭代非基于数组的列表效率不高。但是,使用迭代器进行迭代应该(每个实现都可以提供有效的实现)是。

这是一个如何使用迭代器迭代 2 个列表的示例:

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;

public class IterateTwoListsExample {

    public static void main(String[] args) {
        List<String> firstList = Arrays.asList("Blue","Red","Green");
        List<String> secondList = Arrays.asList("Sky","Roses","Grass");

        forEach(firstList, secondList, (first, second) -> System.out.printf("%s %s%n", first, second));
    }

    public static <T0, T1> void forEach(List<? extends T0> first, List<? extends T1> second, BiConsumer<? super T0, ? super T1> consumer) {
        final Iterator<? extends T0> firstIt = first.iterator();
        final Iterator<? extends T1> secondIt = second.iterator();

        while (firstIt.hasNext() && secondIt.hasNext()) {
            consumer.accept(firstIt.next(), secondIt.next());
        }
    }
}