Back to Blog
Java

Java LinkedList getLast: Retrieve Last Element

java linkedlist getlast: Learn how to use Java LinkedList getLast() to retrieve the last element, its behavior on empty lists, and performance characteristics.

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

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

The LinkedList class in Java provides a getLast() method that returns the last element of the list. This method is part of the Deque interface, which LinkedList implements, and it is a direct way to access the tail of the list without traversing it. If you are working with a LinkedList and need the last element, getLast() is the most straightforward choice, but it comes with a specific behavior that you must understand to avoid runtime exceptions.

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 uses a binary search-like approach to decide whether to start from the head or the tail, but it still iterates through nodes, making 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 are using a List interface reference, you cannot call getLast() directly because List does not declare it. In that case, you would need to cast to Deque or use list.get(list.size() - 1). This is a common source of confusion when developers store a LinkedList in a List variable and then try to access getLast().

Practical Example: Using getLast() in a Stack-Like Workflow

A common use case for getLast() is when you are using a LinkedList as a stack or a queue and need 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 calls getLast(), the behavior is undefined. Use Collections.synchronizedList or a concurrent collection like ConcurrentLinkedDeque if you need thread safety.
  • 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, you will need to update the call because ArrayList does not have getLast(). In that case, 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 the most efficient approach for each implementation. 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: Practical Usage and Code Examples | RYUSLOG DEV