Back to Blog
Java

How to Reverse an ArrayList in Java

java arraylist reverse: Learn how to reverse an ArrayList in Java using Collections.reverse(), manual two-pointer swaps, backward iteration, and ListIterator traversal.

JavaArrayListCollectionsIn-place ReversalListIterator
Illustration of an ArrayList being reversed in place, with elements swapping positions from both ends toward the center.

The java arraylist reverse operation is a common task when working with ordered collections. An ArrayList in Java maintains insertion order, and sometimes the data you receive is in the wrong order for your use case. The most direct answer is Collections.reverse(list), which reverses the order of elements in place. But the right approach depends on whether you can mutate the original list or need a reversed copy.

Using Collections.reverse() for In-Place Reversal

java.util.Collections.reverse() is the standard library method for reversing a List. It operates in place, meaning it modifies the original list rather than creating a new one.

import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; ArrayList<String> names = new ArrayList<>(Arrays.asList("ada", "grace", "linus")); Collections.reverse(names); System.out.println(names); // [linus, grace, ada]

The method accepts any List implementation, not just ArrayList. It swaps elements symmetrically: the first element moves to the last position, the second to the second-to-last, and so on. For an ArrayList, this is an O(n) operation where n is the list size, because element access and assignment are both O(1) for a random-access list.

Manual In-Place Reversal with Two Pointers

If you need to reverse without Collections, or you want to understand the underlying mechanism, a two-pointer swap achieves the same result:

public static <T> void reverseInPlace(ArrayList<T> list) { int left = 0; int right = list.size() - 1; while (left < right) { T temp = list.get(left); list.set(left, list.get(right)); list.set(right, temp); left++; right--; } }

This approach is functionally equivalent to Collections.reverse() for an ArrayList. The loop terminates when the pointers cross, which happens after roughly n/2 iterations. Each iteration performs two reads and two writes, so the total cost is O(n). Writing it manually is rarely necessary in production code, but it clarifies what the library method does internally and is useful in contexts where you want to avoid the Collections utility class.

Creating a Reversed Copy Without Mutating the Original

When the original list must remain unchanged, Collections.reverse() is not suitable because it mutates in place. You need to build a new list. One straightforward way is to copy the list and then reverse the copy:

ArrayList<String> original = new ArrayList<>(Arrays.asList("ada", "grace", "linus")); ArrayList<String> reversed = new ArrayList<>(original); Collections.reverse(reversed); // original is unchanged: [ada, grace, linus] // reversed: [linus, grace, ada]

The copy constructor performs a shallow copy, which is fine for immutable elements. If the list contains mutable objects, both lists reference the same objects; reversing the order does not clone the elements themselves.

An alternative that avoids the intermediate copy is to iterate backward and add each element to a new list:

ArrayList<String> reversed = new ArrayList<>(original.size()); for (int i = original.size() - 1; i >= 0; i--) { reversed.add(original.get(i)); }

Pre-sizing the destination with new ArrayList<>(original.size()) avoids incremental resizing during the loop. Both approaches produce the same result; the copy-then-reverse version reads more clearly, while the backward loop avoids the temporary mutation.

Using ListIterator for Backward Traversal

A ListIterator provides a way to traverse an ArrayList in reverse without relying on index arithmetic. This is useful when you need to process elements in reverse order as you iterate, rather than materializing a reversed list:

ArrayList<String> names = new ArrayList<>(Arrays.asList("ada", "grace", "linus")); ListIterator<String> iterator = names.listIterator(names.size()); while (iterator.hasPrevious()) { String name = iterator.previous(); System.out.println(name); // linus, grace, ada }

The listIterator(int index) method positions the iterator at the given index, and hasPrevious()/previous() move backward through the list. This approach does not modify the list and does not allocate a new collection. It is the right choice when the goal is reverse-order processing rather than a reversed data structure.

Performance and Memory Tradeoffs

The main performance distinction is between in-place reversal and copy-based reversal. In-place reversal via Collections.reverse() allocates no additional storage beyond a single temporary variable for each swap, and runs in O(n) time. Copy-based reversal allocates a second list of the same size, doubling the memory footprint for the duration of the operation, and also runs in O(n) time.

ApproachMutates originalNew allocationTime complexityBest use case
Collections.reverse()YesNoO(n)In-place reversal when mutation is acceptable
Two-pointer manual swapYesNoO(n)Avoiding the Collections utility or learning the mechanism
Copy then reverseNoYes (one list)O(n)Preserving the original while producing a reversed list
Backward loop with pre-sized listNoYes (one list)O(n)Building a reversed list without a temporary mutation
ListIterator backward traversalNoNoO(n)Processing elements in reverse without building a new list

For large lists, the allocation cost of the copy-based approaches is the dominant factor. If the list contains millions of elements, an in-place reversal avoids a large allocation, but it permanently changes the order of the original list. The decision should be driven by whether downstream code depends on the original order.

Edge Cases and Common Mistakes

An empty ArrayList is a valid input for all the approaches above. Collections.reverse() on an empty list is a no-op. The two-pointer loop never executes because left < right is false when the list is empty. The backward loop produces an empty list. The ListIterator positioned at index 0 has no previous element.

A list with a single element is also a no-op for reversal, since there is nothing to swap.

Null elements do not cause problems with any of these approaches, because reversal only moves references; it never dereferences the elements. A list containing null values reverses correctly.

A common mistake is calling Collections.reverse() on a list that is later expected to retain its original order. This is a subtle bug because the reversal succeeds silently, and the corruption of the original order may only surface in a later part of the program. If the original order matters, use a copy-based approach.

Another mistake is using Collections.reverse() on a list that is actually an unmodifiable view, such as Collections.unmodifiableList(...). The method will throw UnsupportedOperationException at runtime because the underlying list does not support set(). This failure is not caught at compile time, so it is worth verifying that the list is mutable before calling the method.

Choosing the Right Approach for Your Use Case

The decision between in-place and copy-based reversal comes down to whether the original list is still needed. If the list is a local variable used only for the current operation, Collections.reverse() is the simplest and most readable choice. If the list is shared with other parts of the application, or if it is a field that other methods read, a copy-based approach avoids introducing a subtle ordering bug.

For reverse-order processing without materializing a reversed list, the ListIterator approach is the most memory-efficient. It is particularly useful in streaming or batch scenarios where the reversed order is consumed once and then discarded.

The manual two-pointer implementation is rarely the best production choice, since Collections.reverse() is standard, tested, and immediately recognizable to other developers. Its main value is educational, or in codebases that deliberately avoid the Collections utility class for consistency reasons.

java arraylist reverse: Practical Usage and Code Examples | RYUSLOG DEV