高效遍历两个相关的ArrayList的方法?

4
我正在寻找一种干净简洁的方法来迭代遍历两个ArrayLists,这两个列表直接相关联,即一个列表中的每个索引映射到另一个列表的索引(存在关系)。
目前,我通过一个简单的for循环来完成迭代遍历,我尝试查看了lambda表达式和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循环?


1
流和Lambda表达式肯定比for循环更糟糕。也许不像我曾经坚决反对并且不悔改的反流派那样糟糕。可能只是稍微糟糕一点。但仍然更糟糕。 - Kevin Anderson
5个回答

8
你已经拥有的代码非常高效(假设你正在使用ArrayList),简洁易读。但是这种情况通常需要一个定义关系的类,比如你的情况:

我正在寻找一种简洁明了的方法来迭代两个直接相关的ArrayLists,其中一个ArrayList的每个索引都映射到另一个ArrayList的索引。

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

3
答案:

dreamcrash的答案是正确的:虽然您循环一对数组的方法可行,但更好的做法是利用Java作为面向对象编程语言来定义自己的类。

记录

在Java 16中,通过新的记录特性,定义这样一个类变得更加简单。当它的主要目的是透明且不可变地传递数据时,将其编写为记录。

默认情况下,记录非常简洁。编译器隐式创建构造函数、getter、equals&hashCodetoString。您只需要声明每个成员字段的类型和名称即可。

record ColorNoun ( String color , String noun ) {}

使用记录就像普通类一样。可以使用new关键字进行实例化。

ColorNoun blueSky = new ColorNoun ( "Blue" , "Sky" ) ;

请注意,记录可以在方法内部声明局部变量,也可以像常规类一样单独声明。

2
你可以使用“range”语句和lambda表达式来迭代索引。

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

Like this:

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表达式。

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


2
为什么人们继续使用 System.out.println(String.format(…)) 而不是 System.out.printf(…)?并不是因为使用 "%s %s" 格式化比更简单和高效的 firstList.get(index) + " " + secondList.get(index) 更好。 - Holger
如果我们通过String.format(...)处理许多操作,那么它的效率非常低下。 - RafalQA

1
如果您的意图只是避免使用for循环,那么您可以尝试以下方法:
Iterator<String> iterator = secondList.iterator();
firstList.forEach(s -> System.out.println(s + " " + iterator.next()));

甚至可以将内容添加到一个 StringBuilder 中,然后最后显示结果。

0
所有其他答案都是正确的,首选解决方案应该始终是由@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());
        }
    }
}


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