简体   繁体   中英

Efficient way to iterate over two related ArrayLists?

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

Currently, I'm doing it via a simple for loop and I tried looking into lambdas and for-each loops but don't see a way to apply it to two lists at the same time that are of the same size.

firstList: ["Blue", "Red", "Green"]

secondList: ["Sky", "Roses", "Grass"]

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

Result:

Blue Sky
Red Roses
Green Grass

Is there a way to effectively iterate over both lists simultaneously using a lambda or a for-each loop to avoid using a for loop?

What you have it is already pretty efficient (assuming that you are using ArrayList), concise and readable. However, this type of scenarios:

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

typically require a class that defines the relationship, in your case:

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 + '\'' +
                '}';
    }
} 

then the two lists would become one:

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

and then you can use:

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

The Answer by dreamcrash is correct: While your looping of a pair of arrays works, you should instead take advantage of Java as a OOP language by defining your own class.

Record

Defining such a class is even simpler in Java 16 with the new records feature. Write your class as a record when it's main purpose is communicating data, transparently and immutably.

A record is very brief by default. The compiler implicitly creates the constructor, getters, equals & hashCode , and toString . You need only declare the type and name of each member field.

record ColorNoun ( String color , String noun ) {}

Use a record like a conventional class. Instantiate with new .

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

Note that a record can be declared locally within a method, or declared separately like a conventional class.

You can use the "range"-statement to iterate the index with a lambda expression.

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

It's basically the same approach - just with lambdas.

The only way to get rid of the index-based access is by using a different data-structure or use an alternating iteration technique as shown in the other answers.

If your intention is to just avoid using a for loop, then you could try the below:

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

Or even add to a StringBuilder and then display the result in the end.

All the other answers are correct and the preferred solution should always be the one shown by @Basil Bourque, but as others pointed out in the comments, iterating a non-array-based list using indexes is not very efficient. However, iterating using an iterator should (where each implementation can provide an efficient implementation) is.

Here is an example how you can iterate 2 lists using their iterator:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM