Back to Blog
Java

Java ListIterator previous: Reverse Traversal Explained

java listiterator previous: Learn how to use ListIterator.previous() to traverse a Java list backward, including hasPrevious(), index handling, and common pitfalls.

ListIteratorJava CollectionsReverse IterationIteratorJava List
Diagram showing a Java ListIterator moving backward through a list with previous() method

java listiterator previous requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

The ListIterator.previous() method is the standard way to move backward through a List in Java. Unlike a regular Iterator, which only supports forward traversal, a ListIterator maintains a cursor that can move in both directions. When you call previous(), it returns the element before the cursor and moves the cursor back one position. This is essential for reverse iteration, bidirectional algorithms, and scenarios where you need to revisit elements without re-creating an iterator.

How previous() Works in ListIterator

The cursor in a ListIterator sits between elements. Initially, when you obtain a ListIterator via list.listIterator(), the cursor is before the first element. Calling next() returns the first element and moves the cursor after it. Calling previous() returns the element before the cursor and moves the cursor before it. This means after a next(), a subsequent previous() returns the same element, allowing you to step back and forth.

The method signature is straightforward:

E previous()

It returns the element immediately before the current cursor position. If the cursor is at the beginning of the list (i.e., hasPrevious() returns false), calling previous() throws a NoSuchElementException. The cursor position after the call is one index lower than before.

Using hasPrevious() to Guard the Call

Always check hasPrevious() before calling previous() to avoid runtime exceptions. The typical reverse traversal loop looks like this:

List<String> items = new ArrayList<>(List.of("A", "B", "C")); ListIterator<String> iterator = items.listIterator(items.size()); // start at the end while (iterator.hasPrevious()) { String item = iterator.previous(); System.out.println(item); }

This prints C, B, A. Starting the iterator at items.size() positions the cursor after the last element, so the first previous() returns the last element. The loop continues until the cursor reaches the beginning.

Index Behavior: previousIndex() and nextIndex()

The ListIterator tracks two indices: nextIndex() returns the index of the element that would be returned by the next call to next(), and previousIndex() returns the index of the element that would be returned by the next call to previous(). These indices are relative to the cursor. After calling previous(), nextIndex() decreases by one, and previousIndex() decreases by one as well. For example:

List<String> list = new ArrayList<>(List.of("X", "Y", "Z")); ListIterator<String> it = list.listIterator(2); // cursor before index 2 System.out.println(it.previousIndex()); // 1 System.out.println(it.nextIndex()); // 2 String y = it.previous(); // returns "Y" System.out.println(it.previousIndex()); // 0 System.out.println(it.nextIndex()); // 1

Understanding these index changes is critical when you need to know the current position after a backward step, especially when adding or removing elements during iteration.

Common Mistakes When Calling previous()

One frequent error is calling previous() without checking hasPrevious(), which throws NoSuchElementException. Another is mixing next() and previous() without tracking the cursor. For instance, calling next() then previous() returns the same element, but calling previous() twice in a row skips an element. This often happens in loops that attempt to alternate directions.

A more subtle issue occurs when modifying the list during iteration. If you call list.remove() or list.add() while using a ListIterator, the iterator's internal modCount is checked. Any structural modification made through the list itself (not through the iterator) will cause a ConcurrentModificationException on the next call to previous() or next(). Use the iterator's own remove() and add() methods to safely modify the list while iterating.

Performance and Memory Considerations

The previous() method runs in constant time for ArrayList and other random-access lists because the underlying structure is an array. For LinkedList, the operation is also O(1) because the iterator holds a reference to the current node; moving backward simply follows the previous pointer. However, creating a new ListIterator from scratch and moving it to a specific index can be O(n) for LinkedList if you use listIterator(index). If you need to traverse backward frequently, consider whether the list implementation matches your access pattern.

Memory overhead is minimal: a ListIterator holds a reference to the list and a cursor index. No additional data structures are created. This makes bidirectional traversal efficient in both time and space for most use cases.

Compatibility and Fail-Fast Behavior

All standard Java collections return ListIterator instances that are fail-fast. This means that if the list is structurally modified after the iterator is created (except through the iterator's own remove() or add() methods), the iterator will throw a ConcurrentModificationException on the next previous() or next() call. This behavior is not guaranteed for all collection implementations; it is an optimization designed to catch bugs early. When working with concurrent collections or custom implementations, verify the iterator's contract.

Also note that ListIterator is an interface in java.util. Any class that implements List can provide a ListIterator, but the exact behavior of previous() depends on the implementation. For example, CopyOnWriteArrayList returns a snapshot iterator that does not throw ConcurrentModificationException, but it also does not reflect concurrent modifications.

Practical Example: Reverse Iteration with ListIterator

The following example demonstrates a common use case: processing a list in reverse order while removing elements that meet a condition. Using ListIterator allows you to remove the current element safely.

import java.util.*; public class ReverseRemoval { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6)); ListIterator<Integer> it = numbers.listIterator(numbers.size()); while (it.hasPrevious()) { int value = it.previous(); if (value % 2 == 0) { it.remove(); // safe removal via iterator } } System.out.println(numbers); // [1, 3, 5] } }

Here, starting from the end and moving backward ensures that removing an element does not skip the next element. If you iterated forward and removed, you would skip elements because the list shifts. This is a classic pattern where previous() provides a cleaner solution than forward iteration with index adjustment.

java listiterator previous: Practical Usage and Code Example | RYUSLOG DEV