Back to Blog
Java

Java LinkedList getLast: Retrieve Last Element

Learn how Java's LinkedList getLast() returns the last element, why it throws on an empty list, and when peekLast() is a safer alternative. Includes code examples and performance notes.

JavaLinkedListCollectionsgetLastData Structures
Java LinkedList getLast method retrieving the last node in a linked list diagram.

Java's LinkedList.getLast() returns the last element of the list without removing it. This method is part of the Deque interface, which LinkedList implements, and it gives direct access to the tail node. This article explains the method's behavior, what happens when the list is empty, and how getLast() compares with peekLast().

Using getLast() on a LinkedList

The getLast() method is defined in the Deque interface and is inherited by LinkedList. It retrieves the last element of the list without removing it. The method signature is simple:

E getLast()

Here is a minimal example:

import java.util.LinkedList; public class GetLastExample { public static void main(String[] args) { LinkedList<String> tasks = new LinkedList<>(); tasks.add("compile"); tasks.add("test"); tasks.add("deploy"); String lastTask = tasks.getLast(); System.out.println(lastTask); // deploy } }

Because LinkedList maintains a reference to both the head and the tail node, getLast() returns the tail node's data directly. There is no iteration involved, and the operation completes in constant time. This is different from using get(index) with list.get(list.size() - 1), which also works but requires an index lookup. For a LinkedList, get(index) is an O(n) operation because the list must traverse from the head or tail depending on the index. Using getLast() avoids that traversal entirely.

What Happens When the List Is Empty

getLast() throws a NoSuchElementException if the list is empty. This is a critical detail because it is an unchecked exception, so the compiler will not force you to handle it. If you call getLast() on an empty list without a try-catch block, your program will terminate with an exception.

LinkedList<Integer> numbers = new LinkedList<>(); // numbers.getLast(); // throws NoSuchElementException

To handle this safely, you can check isEmpty() before calling getLast(), or you can use an alternative method that returns a default value instead of throwing. The Deque interface provides peekLast(), which returns null when the list is empty. This makes peekLast() a safer choice in scenarios where an empty list is a normal condition.

getLast() vs peekLast(): Choosing the Right Method

The choice between getLast() and peekLast() depends on how you want to handle the empty-list case. Both methods return the last element without removing it, but they differ in their failure behavior.

MethodEmpty List BehaviorReturn TypeUse Case
getLast()Throws NoSuchElementExceptionEWhen an empty list is an error condition
peekLast()Returns nullE (nullable)When an empty list is a valid state

If your logic treats an empty list as an exceptional case, getLast() is appropriate because it fails fast. If the list being empty is a normal business scenario, peekLast() avoids the exception overhead and lets you handle null explicitly. Note that peekLast() can also return null if the list contains null elements, so you may need to distinguish between an empty list and a list whose last element is null. In practice, if your list can contain null values, consider using isEmpty() before calling getLast() to avoid ambiguity.

Performance: Why getLast() Is O(1) for LinkedList

LinkedList is implemented as a doubly-linked list with references to both the first and last nodes. The getLast() method simply returns the data of the tail node, so its time complexity is O(1). This is a significant advantage over using list.get(list.size() - 1), which for a LinkedList must traverse the list from the head or tail depending on the index. The get(int) method in LinkedList checks whether the index is in the first half or the second half of the list, then walks node by node from the closer end; that traversal makes it O(n) in the worst case.

Memory-wise, getLast() does not allocate new objects; it only returns a reference to an existing element. This is important in performance-sensitive code where frequent access to the last element is required. If you are repeatedly retrieving the last element of a LinkedList, using getLast() instead of get(size()-1) can reduce CPU cycles, especially for large lists.

Retrieving the Last Element in Other Collections

If you are using an ArrayList, the equivalent operation is list.get(list.size() - 1). Since ArrayList is backed by an array, this is an O(1) operation as well. However, the syntax is less expressive, and you must ensure the list is not empty to avoid an IndexOutOfBoundsException. For a LinkedList, getLast() is the idiomatic method because it clearly conveys the intent of accessing the tail.

For ArrayDeque, which also implements Deque, getLast() works similarly and is O(1). If you store a LinkedList in a List interface reference, the situation depends on your Java version. Before Java 21, List did not declare getLast(), so you had to cast the variable to Deque or use list.get(list.size() - 1). Since Java 21, List includes getFirst() and getLast(), so a List variable can call getLast() directly. This distinction is a common source of confusion when developers store a LinkedList in a List variable and then try to access getLast().

Practical Example: Inspecting the Last Element in a Stack-Like Workflow

A common need when using a LinkedList as a stack or a queue is to inspect the most recently added element without removing it. For example, consider an undo stack that stores command objects. You might want to see the last command before deciding whether to undo it.

import java.util.LinkedList; class Command { private final String action; Command(String action) { this.action = action; } String getAction() { return action; } } public class UndoStack { private final LinkedList<Command> history = new LinkedList<>(); public void push(Command command) { history.addLast(command); } public Command peekLastCommand() { // Returns null if the stack is empty return history.peekLast(); } public Command popLastCommand() { return history.removeLast(); } public boolean isEmpty() { return history.isEmpty(); } }

In this example, peekLast() is used to avoid throwing an exception when the stack is empty. If the business logic guarantees that the stack is never empty when you call getLast(), you could use getLast() directly. The key is to choose the method that matches your error-handling strategy.

Edge Cases and Maintainability Considerations

When using getLast(), be aware of the following edge cases:

  • Empty list: Always check isEmpty() or use peekLast() if an empty list is possible.
  • Null elements: If the list can contain null, peekLast() returns null both for an empty list and for a list whose last element is null. To distinguish, check isEmpty() first.
  • Concurrent modification: LinkedList is not thread-safe. If multiple threads modify the list while another thread reads it, the result is not guaranteed. Synchronize access externally or use a concurrent Deque such as ConcurrentLinkedDeque.
  • Type erasure: When using a generic LinkedList, getLast() returns the type parameter. If you are using raw types, you may need an explicit cast, which can lead to ClassCastException.

From a maintainability perspective, using getLast() makes the code more readable than get(size()-1) because it directly expresses the intent. It also avoids the risk of off-by-one errors. However, if you later change the collection type to an ArrayList, the exact change depends on your Java version: before Java 21, ArrayList does not have getLast(), so you would need to update the call; since Java 21, a List-typed variable can call getLast() on either collection. A helper method that abstracts the last-element access can reduce the impact of such changes.

For example, you could define a utility method:

public static <T> T getLastOrNull(List<T> list) { if (list.isEmpty()) { return null; } if (list instanceof LinkedList) { return ((LinkedList<T>) list).getLast(); } return list.get(list.size() - 1); }

This method works for any List and uses LinkedList's O(1) getLast() when possible, falling back to get(size() - 1) for other list implementations. While such a utility is not necessary for small projects, it can be valuable in codebases where collection types are swapped frequently. The important thing is to understand the behavior of getLast() and its alternatives so you can make an informed choice for your specific use case.

Java LinkedList getLast(): Retrieve the Last Element Without Removing It | RYUSLOG DEV