Start learning free

LESSON 115 OF 410 · FOR LOOPS - LOOPING OVER ARRAYS

Infinite For Loops II in Java

Lesson Read the full explanation

We also use for loops to iterate over sequences like strings and arrays. We can run into problems when we manipulate a sequence while iterating on it.

For example, if we append elements to an array while iterating on it:

List<String> fruits = new ArrayList<>(List.of("banana", "orange"));

for(var fruit : fruits) {
  fruits.add("kivi");
}

Every time we enter this loop, we add a kivi item to the end of the list that we are iterating through, so we would never make it to the end of the list: it would keep growing forever.

Java stops this for us: after the first item is added, the loop's next step throws a ConcurrentModificationException (the list was changed while the loop was going through it).

Changing a collection while looping over it is always a bug. And when nothing stops the loop, the collection keeps growing forever: an infinite loop. As programmers, we want to make sure we never write infinite loops, as they make our program run forever and completely unusable.


Assignment
Follow the Coding Tutorial and let's practice with infinite for loops!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 1
    Ctrl + Enter to check

    JAVA · FOR LOOPS - LOOPING OVER ARRAYS

    Infinite For Loops 2 in Java, explained

    Introduction

    In this lesson, we will explore the concept of infinite for loops in Java, particularly when iterating over sequences like arrays and lists. Understanding how to avoid infinite loops is crucial for writing efficient and functional code. Infinite loops can cause programs to become unresponsive and consume unnecessary resources.

    Understanding the Basics

    Before diving into the main concepts, let's understand the basics of for loops and how they work. A for loop is used to iterate over a sequence (like an array or a list) and execute a block of code multiple times. Here is a simple example:

    for (int i = 0; i < 5; i++) {
        System.out.println(i);
    }
    

    This loop will print numbers from 0 to 4. The loop runs as long as the condition i < 5 is true.

    Main Concepts

    When iterating over a sequence, modifying the sequence (like adding or removing elements) can lead to unexpected behavior, such as infinite loops. In the example provided, adding an element to the list while iterating over it causes the loop to never terminate:

    List<String> fruits = new ArrayList<>(List.of("banana", "orange"));
    
    for (var fruit : fruits) {
        fruits.add("kivi");
    }
    

    Each iteration adds a new element, causing the list to grow indefinitely.

    Examples and Use Cases

    Let's look at a few examples to understand how to avoid infinite loops:

    // Example 1: Using a traditional for loop
    List<String> fruits = new ArrayList<>(List.of("banana", "orange"));
    
    for (int i = 0; i < fruits.size(); i++) {
        if (fruits.get(i).equals("banana")) {
            fruits.add("kivi");
        }
    }
    

    In this example, we use a traditional for loop with an index. This approach can still lead to an infinite loop if the condition for adding elements is met frequently.

    // Example 2: Using an iterator
    List<String> fruits = new ArrayList<>(List.of("banana", "orange"));
    Iterator<String> iterator = fruits.iterator();
    
    while (iterator.hasNext()) {
        String fruit = iterator.next();
        if (fruit.equals("banana")) {
            iterator.remove();
        }
    }
    

    Using an iterator allows us to safely remove elements while iterating without causing an infinite loop.

    Common Pitfalls and Best Practices

    Here are some common pitfalls to avoid and best practices to follow:

    • Avoid modifying the sequence (adding/removing elements) directly within a for-each loop.
    • Use iterators when you need to modify the sequence during iteration.
    • Ensure loop conditions will eventually become false to avoid infinite loops.

    Advanced Techniques

    For more advanced scenarios, consider using concurrent collections or other synchronization mechanisms when dealing with multi-threaded environments. This ensures thread safety and prevents infinite loops caused by concurrent modifications.

    Code Implementation

    Here is a well-commented code snippet demonstrating the correct use of iterators to avoid infinite loops:

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class InfiniteLoopExample {
        public static void main(String[] args) {
            // Initialize the list with some fruits
            List<String> fruits = new ArrayList<>(List.of("banana", "orange"));
    
            // Use an iterator to safely remove elements while iterating
            Iterator<String> iterator = fruits.iterator();
            while (iterator.hasNext()) {
                String fruit = iterator.next();
                if (fruit.equals("banana")) {
                    iterator.remove(); // Safe removal
                }
            }
    
            // Print the modified list
            System.out.println(fruits);
        }
    }
    

    Debugging and Testing

    When debugging infinite loops, use breakpoints and step through the code to understand the loop's behavior. Writing tests can help ensure your loops terminate as expected. Here is an example of a simple test case:

    import org.junit.jupiter.api.Test;
    import static org.junit.jupiter.api.Assertions.*;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class InfiniteLoopTest {
        @Test
        public void testLoopTermination() {
            List<String> fruits = new ArrayList<>(List.of("banana", "orange"));
            // Call the method that modifies the list
            modifyList(fruits);
            // Check that the list does not contain "banana"
            assertFalse(fruits.contains("banana"));
        }
    
        private void modifyList(List<String> fruits) {
            Iterator<String> iterator = fruits.iterator();
            while (iterator.hasNext()) {
                String fruit = iterator.next();
                if (fruit.equals("banana")) {
                    iterator.remove();
                }
            }
        }
    }
    

    Thinking and Problem-Solving Tips

    When approaching problems related to loops, break down the problem into smaller parts. Ensure you understand the loop's termination condition and how modifications to the sequence affect the loop. Practice with different scenarios to build a strong understanding.

    Conclusion

    In this lesson, we covered the concept of infinite for loops in Java, how they occur, and how to avoid them. By understanding the basics, applying best practices, and using appropriate techniques, you can write efficient and bug-free code. Keep practicing and exploring further applications to master these concepts.

    Additional Resources

    Keep going with JavaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free