707

Being somewhat new to the Java language I'm trying to familiarize myself with all the ways (or at least the non-pathological ones) that one might iterate through a list (or perhaps other collections) and the advantages or disadvantages of each.

Given a List<E> list object, I know of the following ways to loop through all elements:

Basic for loop (of course, there're equivalent while / do while loops as well)

// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
    E element = list.get(i);
    // 1 - can call methods of element
    // 2 - can use 'i' to make index-based calls to methods of list

    // ...
}

Note: As @amarseillan pointed out, this form is a poor choice for iterating over Lists, because the actual implementation of the get method may not be as efficient as when using an Iterator. For example, LinkedList implementations must traverse all of the elements preceding i to get the i-th element.

In the above example there's no way for the List implementation to "save its place" to make future iterations more efficient. For an ArrayList it doesn't really matter, because the complexity/cost of get is constant time (O(1)) whereas for a LinkedList is it proportional to the size of the list (O(n)).

For more information about the computational complexity of the built-in Collections implementations, check out this question.

Enhanced for loop (nicely explained in this question)

for (E element : list) {
    // 1 - can call methods of element

    // ...
}

Iterator

for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
    E element = iter.next();
    // 1 - can call methods of element
    // 2 - can use iter.remove() to remove the current element from the list

    // ...
}

ListIterator

for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
    E element = iter.next();
    // 1 - can call methods of element
    // 2 - can use iter.remove() to remove the current element from the list
    // 3 - can use iter.add(...) to insert a new element into the list
    //     between element and iter->next()
    // 4 - can use iter.set(...) to replace the current element

    // ...
}

Functional Java

list.stream().map(e -> e + 1); // Can apply a transformation function for e

Iterable.forEach, Stream.forEach, ...

(A map method from Java 8's Stream API (see @i_am_zero's answer).)

In Java 8 collection classes that implement Iterable (for example, all Lists) now have a forEach method, which can be used instead of the for loop statement demonstrated above. (Here is another question that provides a good comparison.)

Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
//     (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
//     being performed with each item.

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).

What other ways are there, if any?

(BTW, my interest does not stem at all from a desire to optimize performance; I just want to know what forms are available to me as a developer.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jacobq
  • 11,209
  • 4
  • 40
  • 71
  • 1
    Those are the non-pathological ones, although you might also use any of several functional-style libraries to process collections as well. – Dave Newton Aug 23 '13 at 19:05
  • Is the question about the `List` interface specifically? – Sotirios Delimanolis Aug 23 '13 at 19:08
  • @SotiriosDelimanolis, for all intents and purposes, yes it is specific to List, but if there are other interesting ways to work with, say, a Collection, I'd be interested to know them. – jacobq Aug 23 '13 at 19:11
  • @DaveNewton, thanks for the idea. I've never used anything like that. Please take a look at my edited question and let me know if I understood what you meant. – jacobq Aug 23 '13 at 19:12
  • I think your understanding of the enhanced `for` loop is wrong. Inside the loop, `element` is a reference to the actual list element, not a copy of it. Similarly with the iterator. You can, in fact, use all three styles to iterate over lists of singletons (or even enum values) that _cannot_ be copied. – Ted Hopp Aug 23 '13 at 19:15
  • @Ted, in that case, could you explain why the iterator / for-each portions in the ListIterationExample.java do not cause `numbers` to change? – jacobq Aug 23 '13 at 19:17
  • I think you should scale down your question and post those examples as an answer. – sdasdadas Aug 23 '13 at 19:17
  • Should we be talking about Java 8? It's still one year away from a reality! – Mario Rossi Aug 23 '13 at 19:26
  • 2
    @sdasdadas, done: http://stackoverflow.com/questions/18410035/ways-to-iterate-over-a-list-in-java/18410379#answer-18410379 – jacobq Aug 23 '13 at 19:26
  • @MarioRossi, yes, please feel free to list additional techniques offered in Java 8. Perhaps using [the Stream API](http://download.java.net/jdk8/docs/api/java/util/stream/package-summary.html "Java 8 Stream API")? – jacobq Aug 23 '13 at 19:29

13 Answers13

330

The three forms of looping are nearly identical. The enhanced for loop:

for (E element : list) {
    . . .
}

is, according to the Java Language Specification, identical in effect to the explicit use of an iterator with a traditional for loop. In the third case, you can only modify the list contents by removing the current element and, then, only if you do it through the remove method of the iterator itself. With index-based iteration, you are free to modify the list in any way. However, adding or removing elements that come before the current index risks having your loop skipping elements or processing the same element multiple times; you need to adjust the loop index properly when you make such changes.

In all cases, element is a reference to the actual list element. None of the iteration methods makes a copy of anything in the list. Changes to the internal state of element will always be seen in the internal state of the corresponding element on the list.

Essentially, there are only two ways to iterate over a list: by using an index or by using an iterator. The enhanced for loop is just a syntactic shortcut introduced in Java 5 to avoid the tedium of explicitly defining an iterator. For both styles, you can come up with essentially trivial variations using for, while or do while blocks, but they all boil down to the same thing (or, rather, two things).

EDIT: As @iX3 points out in a comment, you can use a ListIterator to set the current element of a list as you are iterating. You would need to use List#listIterator() instead of List#iterator() to initialize the loop variable (which, obviously, would have to be declared a ListIterator rather than an Iterator).

Olivier
  • 303
  • 3
  • 14
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • OK, thanks, but if the iterator is actually returning a reference to the real element (not a copy) then how can I use it to change the value in the list? I take that if I use an `e = iterator.next()` then doing `e = somethingElse` just changes what object `e` is referring to rather than changing the actual store from which `iterator.next()` retrieved the value. – jacobq Aug 23 '13 at 19:34
  • @iX3 - That would be true of an index-based iteration as well; assigning a new object to `e` won't change what's in the list; you have to call `list.set(index, thing)`. You can change the _contents_ of `e` (e.g., `e.setSomething(newValue)`), but to change what element is stored in the list as you are iterating it, you need to stick with an index-based iteration. – Ted Hopp Aug 23 '13 at 19:58
  • Thank you for that explanation; I think I understand what you are saying now and will update my question / comments accordingly. There's no "copying" per-se, but because of the language design in order to make changes to the contents of `e` I'd have to call one of `e`'s methods because assignment just changes the pointer (pardon my C). – jacobq Aug 23 '13 at 20:03
  • So for lists of immutable types like `Integer` and `String` it would not be possible to change the contents using `for-each` or `Iterator` methods -- would have to manipulate the list object itself to replace to elements. Is that right? – jacobq Aug 23 '13 at 20:21
  • @iX3 - Correct. You can remove elements with the third form (explicit iterator), but you can only add elements to or replace elements in the list if you use an index-based iteration. – Ted Hopp Aug 23 '13 at 20:25
  • Thanks again. BTW, it looks like there is a special `ListIterator` that allows replacement via `set`. I'm going to add this to the list too. – jacobq Aug 23 '13 at 20:29
  • @iX3 - Yes, a `ListIterator` provides both `set` and `add`. I should have thought of that. – Ted Hopp Aug 23 '13 at 20:43
53

Example of each kind listed in the question:

ListIterationExample.java

import java.util.*;

public class ListIterationExample {

     public static void main(String []args){
        List<Integer> numbers = new ArrayList<Integer>();

        // populates list with initial values
        for (Integer i : Arrays.asList(0,1,2,3,4,5,6,7))
            numbers.add(i);
        printList(numbers);         // 0,1,2,3,4,5,6,7

        // replaces each element with twice its value
        for (int index=0; index < numbers.size(); index++) {
            numbers.set(index, numbers.get(index)*2); 
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14

        // does nothing because list is not being changed
        for (Integer number : numbers) {
            number++; // number = new Integer(number+1);
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14  

        // same as above -- just different syntax
        for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            number++;
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14

        // ListIterator<?> provides an "add" method to insert elements
        // between the current element and the cursor
        for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            iter.add(number+1);     // insert a number right before this
        }
        printList(numbers);         // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

        // Iterator<?> provides a "remove" method to delete elements
        // between the current element and the cursor
        for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            if (number % 2 == 0)    // if number is even 
                iter.remove();      // remove it from the collection
        }
        printList(numbers);         // 1,3,5,7,9,11,13,15

        // ListIterator<?> provides a "set" method to replace elements
        for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            iter.set(number/2);     // divide each element by 2
        }
        printList(numbers);         // 0,1,2,3,4,5,6,7
     }

     public static void printList(List<Integer> numbers) {
        StringBuilder sb = new StringBuilder();
        for (Integer number : numbers) {
            sb.append(number);
            sb.append(",");
        }
        sb.deleteCharAt(sb.length()-1); // remove trailing comma
        System.out.println(sb.toString());
     }
}
jacobq
  • 11,209
  • 4
  • 40
  • 71
27

The basic loop is not recommended as you do not know the implementation of the list.

If that was a LinkedList, each call to

list.get(i)

would be iterating over the list, resulting in N^2 time complexity.

amarseillan
  • 472
  • 5
  • 12
  • 1
    Correct; that is a good point, and I will update the example in the question. – jacobq Apr 08 '15 at 20:36
  • according to this post, your statement is not correct :[Which is more efficient, a for-each loop, or an iterator?](http://stackoverflow.com/questions/2113216/which-is-more-efficient-a-for-each-loop-or-an-iterator) – Hibbem Oct 17 '16 at 12:14
  • 7
    What I read in that post is exactly what I said... What part are you reading exactly? – amarseillan Oct 18 '16 at 21:07
26

A JDK8-style iteration:

public class IterationDemo {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3);
        list.stream().forEach(elem -> System.out.println("element " + elem));
    }
}
eugene82
  • 8,432
  • 2
  • 22
  • 30
  • Thanks, I intend to update the list at the top with Java 8 solutions eventually. – jacobq Apr 07 '14 at 19:39
  • 1
    @eugene82 is this approach much more efficient? – catch23 Dec 14 '14 at 11:37
  • 1
    @nazar_art You're not going to see much improvement over anything else unless you perhaps used parallelStream on a very large collection. It's more efficient in the sense of healing verbosity only, really. – Rogue Dec 27 '14 at 01:57
9

In Java 8 we have multiple ways to iterate over collection classes.

Using Iterable forEach

The collections that implement Iterable (for example all lists) now have forEach method. We can use method-reference introduced in Java 8.

Arrays.asList(1,2,3,4).forEach(System.out::println);

Using Streams forEach and forEachOrdered

We can also iterate over a list using Stream as:

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println);

We should prefer forEachOrdered over forEach because the behaviour of forEach is explicitly nondeterministic where as the forEachOrdered performs an action for each element of this stream, in the encounter order of the stream if the stream has a defined encounter order. So forEach does not guarantee that the order would be kept.

The advantage with streams is that we can also make use of parallel streams wherever appropriate. If the objective is only to print the items irrespective of the order then we can use parallel stream as:

Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println);
akhil_mittal
  • 23,309
  • 7
  • 96
  • 95
6

I don't know what you consider pathological, but let me provide some alternatives you could have not seen before:

List<E> sl= list ;
while( ! sl.empty() ) {
    E element= sl.get(0) ;
    .....
    sl= sl.subList(1,sl.size());
}

Or its recursive version:

void visit(List<E> list) {
    if( list.isEmpty() ) return;
    E element= list.get(0) ;
    ....
    visit(list.subList(1,list.size()));
}

Also, a recursive version of the classical for(int i=0... :

void visit(List<E> list,int pos) {
    if( pos >= list.size() ) return;
    E element= list.get(pos) ;
    ....
    visit(list,pos+1);
}

I mention them because you are "somewhat new to Java" and this could be interesting.

Mario Rossi
  • 7,651
  • 27
  • 37
  • 1
    Thanks for mentioning it. I had never looked at the subList method. Yes, I would consider it pathological as I don't know of any circumstance where it would ever be advantageous to use this aside from perhaps obfuscation contests. – jacobq Aug 23 '13 at 19:25
  • 3
    OK. I don't like being called "pathological" so here goes one explanation: I have used them while tracking or following paths in trees. One more? While translating some Lisp programs to Java I didn't want them to lose their Lisp spirit, and did the same. Upvote the comment if you think these are valid, non-pathological uses. I need a group hug!!! :-) – Mario Rossi Aug 23 '13 at 19:32
  • Aren't paths in trees different than lists? BTW, I didn't mean to call you or any person "pathological" I just mean that some answers to my question will be understandably impractical or very unlikely to have value in good software engineering practice. – jacobq Aug 23 '13 at 19:37
  • @iX3 Don't worry; I was just joking. Paths are lists: "start at the root" (obvious), "move to 2nd child", "move to 1st child", "move to 4th child". "Stop". Or [2,1,4] for short. This is a list. – Mario Rossi Aug 23 '13 at 19:57
2

You can use forEach starting from Java 8:

 List<String> nameList   = new ArrayList<>(
            Arrays.asList("USA", "USSR", "UK"));

 nameList.forEach((v) -> System.out.println(v));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sudip Bhandari
  • 2,165
  • 1
  • 27
  • 26
1

In java 8 you can use List.forEach() method with lambda expression to iterate over a list.

import java.util.ArrayList;
import java.util.List;

public class TestA {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("Apple");
        list.add("Orange");
        list.add("Banana");
        list.forEach(
                (name) -> {
                    System.out.println(name);
                }
        );
    }
}
Dil.
  • 1,996
  • 7
  • 41
  • 68
  • Cool. Could you explain how this is different from [`eugene82`'s answer](https://stackoverflow.com/questions/18410035/ways-to-iterate-over-a-list-in-java/52205309#answer-22897118) and [`i_am_zero`'s answer](https://stackoverflow.com/questions/18410035/ways-to-iterate-over-a-list-in-java/52205309#answer-37409771)? – jacobq Sep 10 '18 at 18:50
  • @iX3 ahh. Didn't read the whole answer list. Was trying to be helpful.. Do you want to me to remove the answer? – Dil. Oct 02 '18 at 06:05
  • Doesn't matter to me, though perhaps you could improve it by comparing and contrasting it to other techniques. Or if you think it doesn't demonstrate any new technique but might still be useful as a reference for others, perhaps you could add a note explaining that. – jacobq Oct 04 '18 at 17:33
1

In Java 8 or above, you can iterate a Hashset using forEach() method.

import java.util.HashSet;

public class HashSetTest {
    public static void main(String[] args) {
        
         HashSet<String> hSet = new HashSet<String>();
          
         // Adding elements into your HashSet usind add()
         hSet.add("test1");
         hSet.add("test2");
         hSet.add("test3");
      
         // Iterating over hash set items
         hSet.forEach(x -> System.out.println(x));
         // Or you can write shorter:
         hSet.forEach(System.out::println);
    }
}
jacobq
  • 11,209
  • 4
  • 40
  • 71
Shila Mosammami
  • 999
  • 6
  • 20
  • Could you elaborate about how this differs from `Iterable.forEach` mentioned in the question? `HashSet` is an implementation of `Iterable`. (http://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-) – jacobq Jan 13 '22 at 17:23
  • As far as I know both are use to traverse the collection but in case someone needs to "MODIFY" the collection then an iterator should be used. forEach loop would throw a Concurrent Modification Exception since it does use iterator on the back scene. – Shila Mosammami Jan 13 '22 at 18:11
  • What I was trying to say is that your answer looks like a duplicate of https://stackoverflow.com/questions/18410035/ways-to-iterate-over-a-list-in-java/52205309#answer-37409771. Is there something you are trying to add that has not already been said there (and listed in the "Iterable.forEach, Stream.forEach, ..." part of the question)? – jacobq Jan 19 '22 at 16:51
  • I was just trying to be helpful if I might be. if you believe it is not suitable I can delete it. stackoverflow has been fruitful for me and the purpose I follow, and I like to do as much as I can for the others. thanks anyway – Shila Mosammami Jan 21 '22 at 08:28
  • 1
    I personally don't think that this answer adds anything new (not already discussed), but I'll leave it to others to vote. I appreciate the spirit in which it is offered. – jacobq Jan 24 '22 at 16:37
0

Right, many alternatives are listed. The easiest and cleanest would be just using the enhanced for statement as below. The Expression is of some type that is iterable.

for ( FormalParameter : Expression ) Statement

For example, to iterate through, List<String> ids, we can simply so,

for (String str : ids) {
    // Do something
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
BJYC
  • 354
  • 2
  • 10
  • 4
    He is asking if there any other ways besides the ones described at the question (which include this one you mentioned)! – ericbn Aug 25 '14 at 21:33
0

For a backward search you should use the following:

for (ListIterator<SomeClass> iterator = list.listIterator(list.size()); iterator.hasPrevious();) {
    SomeClass item = iterator.previous();
    ...
    item.remove(); // For instance.
}

If you want to know a position, use iterator.previousIndex(). It also helps to write an inner loop that compares two positions in the list (iterators are not equal).

CoolMind
  • 26,736
  • 15
  • 188
  • 224
0

Above you'll find all differents ways to iterate over a LIST.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class test1 {

public static void main(String[] args) {
    //******* Exercise 1 : Write a Java program to create a new array list, add some colors (string) and print out the collection.
    List<String> colors = new ArrayList<String>();
    colors.add("Black");
    colors.add("Red");
    colors.add("Green");
    colors.add("Blue");
    System.out.println(colors);
    
    
    //******* Exercise 2 : Write a Java program to iterate through all elements in a array list. 
    System.out.println("//******* Exercise 2");
    List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    
    // iteration type 1 : using FOR loop
    System.out.println("// iteration type 1");
    for(Integer nb : list2) {
        System.out.print(nb + ", ");
    }
    System.out.println("\n");
    
    // iteration type 2 : using FOR loop
    System.out.println("// iteration type 2");
    for(int i=0; i < list2.size(); i++) {
        System.out.print(list2.get(i) + ", ");
    }System.out.println("\n");
    
    // iteration type 3  : using Do-While loop
    System.out.println("// iteration type 3");
    int index21 = 0;
    
    do {
        System.out.print(list2.get(index21) + ", ");
        index21++;
    }while(index21<list2.size());
    System.out.println("\n");
    
    
    // iteration type 4  : using While loop
    System.out.println("// iteration type 4");
    int index22 = 0;
    while(index22<list2.size()) {
        System.out.print(list2.get(index22) + ", ");
        index22++;
    }

    System.out.println("\n");
    
    
    // iteration type 5  : using  Iterable forEach loop 
    System.out.println("// iteration type 5");
     list2.forEach(elt -> {
         System.out.print(elt + ", ");
     });

    System.out.println("\n");
    
    
    // iteration type 6  : using  Iterator
    System.out.println("// iteration type 6");
    Iterator<Integer> listIterator = list2.iterator();
    while(listIterator.hasNext()) {
        System.out.print( listIterator.next() + ", ");
    }
    
    System.out.println("\n");
    
    // iteration type 7  : using  Iterator (From the beginning)
    System.out.println("// iteration type 7");
    ListIterator<Integer> listIterator21 = list2.listIterator(list2.size());
    while(listIterator21.hasPrevious()) {
        System.out.print( listIterator21.previous() + ", ");
    }

    System.out.println("\n");   
    
    // iteration type 8  : using  Iterator (From the End)
    System.out.println("// iteration type 8");
    ListIterator<Integer> listIterator22 = list2.listIterator();
    while(listIterator22.hasNext()) {
        System.out.print( listIterator22.next() + ", ");
    }

    System.out.println("\n");   
}

}
  • This looks like https://www.w3resource.com/java-exercises/collection/java-collection-exercise-1.php All the approaches used here appear to be already described in the question or older answers. What new information are you adding here? – jacobq Jan 13 '22 at 17:27
-3

You could always switch out the first and third examples with a while loop and a little more code. This gives you the advantage of being able to use the do-while:

int i = 0;
do{
 E element = list.get(i);
 i++;
}
while (i < list.size());

Of course, this kind of thing might cause a NullPointerException if the list.size() returns 0, becuase it always gets executed at least once. This can be fixed by testing if element is null before using its attributes / methods tho. Still, it's a lot simpler and easier to use the for loop

shieldgenerator7
  • 1,507
  • 17
  • 22
  • True...I guess that is technically different, but the behavior is only different if the list is empty, right? – jacobq Aug 23 '13 at 19:15
  • @ix3 yes, and in that case, the behavior is worse. I wouldn't call this a good alternative. – CPerkins Aug 23 '13 at 19:30
  • Sure, but in fairness, this question is less about what is "good" and more about what is "possible", so I still appreciate this answer. – jacobq Aug 23 '13 at 19:31