Back to Blog
Java

Java LinkedList add: Methods, Performance, and Examples

java linkedlist add: Learn how to add elements to a Java LinkedList using add(), add(int, E), addFirst, and addLast, including performance and edge cases.

Java LinkedListadd methodcollectionsdata structuresperformance
Diagram of Java LinkedList nodes with a new element being appended at the tail

When you call add on a Java LinkedList, the behavior depends on which overload you use and where the new element lands. The java linkedlist add operation is not a single method but a family of methods that differ in insertion position, return value, and time complexity. Understanding these differences is essential for writing efficient and correct code, especially when the list grows or is accessed concurrently.

The add Methods on Java LinkedList

LinkedList implements the List and Deque interfaces, so it inherits add methods from both. The most commonly used are:

  • boolean add(E e) — appends the element to the end of the list.
  • void add(int index, E element) — inserts the element at the specified position.
  • void addFirst(E e) — inserts the element at the head of the list.
  • void addLast(E e) — inserts the element at the tail (equivalent to add(e)).
  • boolean offerFirst(E e) and boolean offerLast(E e) — deque-style additions that return true on success.

Each method has a specific use case. The plain add(E) is the most common because it matches the List contract. The index-based variant gives you control over position but costs more. The addFirst and addLast methods are direct and avoid the ambiguity of index-based insertion.

How add(E) Appends to the Tail

The add(E) method appends the new element to the end of the list. Internally, LinkedList maintains references to the first and last nodes. When you call add(e), a new node is created, its previous pointer is set to the current tail, and the tail reference is updated. Because the tail is always known, this operation runs in constant time, O(1), without needing to traverse the list.

LinkedList<String> list = new LinkedList<>(); list.add("first"); list.add("second"); list.add("third"); System.out.println(list); // [first, second, third]

The method returns true because LinkedList permits duplicates and null values. This return value is part of the Collection contract and is rarely used in practice, but it matters when you rely on the return type in generic code.

Inserting at a Specific Index

When you need to place an element at a specific position, use add(int index, E element). This method first checks that the index is within bounds (0 to size, inclusive). If the index is valid, it traverses the list from either the head or the tail, depending on which side is closer to the target index, and then inserts the new node. This traversal makes the operation O(n) in the worst case.

LinkedList<String> list = new LinkedList<>(); list.add("A"); list.add("C"); list.add(1, "B"); // inserts B between A and C System.out.println(list); // [A, B, C]

If the index is equal to the current size, the element is appended to the end. If it is zero, it is prepended. Any index outside [0, size] throws IndexOutOfBoundsException. This behavior is identical to ArrayList's index-based insert, but the underlying cost differs because LinkedList must traverse node references instead of shifting array elements.

Adding to the Head and Tail Explicitly

For scenarios where you frequently add to the front or back, addFirst and addLast are more explicit and slightly faster than using index-based calls. addFirst creates a new node, sets its next pointer to the current head, and updates the head reference. addLast is identical to add(E) and updates the tail reference. Both run in O(1).

LinkedList<Integer> deque = new LinkedList<>(); deque.addFirst(1); deque.addLast(2); deque.addFirst(0); System.out.println(deque); // [0, 1, 2]

These methods are part of the Deque interface, so they are available on any Deque implementation, not just LinkedList. If you need a queue-like structure, offerFirst and offerLast return false when the capacity is restricted, but LinkedList is unbounded, so they always return true.

Performance and Memory Tradeoffs

The primary advantage of LinkedList.add over ArrayList.add is that inserting at the head or tail is always O(1), whereas ArrayList must shift elements when inserting at the head or middle. However, this advantage comes with a memory cost. Each node in a LinkedList stores the element plus two references (next and previous), adding roughly 16–24 bytes of overhead per element on a 64-bit JVM with compressed OOPs. For large lists, this overhead can be significant.

Another tradeoff is cache locality. ArrayList stores elements contiguously, so iterating over it benefits from CPU cache prefetching. LinkedList nodes are scattered across memory, causing more cache misses. In practice, ArrayList often outperforms LinkedList even for operations that should favor linked structures, unless you are doing many insertions at the head or tail and the list is large enough to make shifting costly.

If you need to add at both ends frequently, ArrayDeque is often a better choice than LinkedList because it uses a resizable array and provides O(1) add/remove at both ends without node overhead. Use LinkedList when you specifically need list semantics (indexed access, null elements) or when you are already using it as a queue and need to iterate with ListIterator.

Edge Cases and Common Mistakes

A common mistake is assuming that add(int, E) is O(1) because LinkedList is a linked structure. It is not; the traversal to find the insertion point is O(n). Another mistake is ignoring the IndexOutOfBoundsException when the index is negative or larger than the current size. Always validate the index if it comes from user input or an external source.

Null elements are allowed in LinkedList, but this can cause subtle bugs if your code later assumes non-null values. For example, using contains(null) or iterating with a null check becomes necessary. If your application forbids nulls, consider using Objects.requireNonNull before adding.

Concurrent modification is another concern. LinkedList is not thread-safe. If multiple threads add or remove elements without external synchronization, the internal node links can become corrupted, leading to infinite loops or NullPointerException. Use Collections.synchronizedList or a concurrent collection like ConcurrentLinkedDeque for multithreaded access.

Choosing Between LinkedList and ArrayList

Use LinkedList.add when you need frequent insertions at the head or tail and the list size is large enough that ArrayList's shifting cost becomes noticeable. For most other cases, ArrayList is simpler and more memory-efficient. If you need indexed access, ArrayList provides O(1) get, while LinkedList requires O(n) traversal. If you are using a Deque and do not need list-specific features, ArrayDeque is usually preferable.

A practical rule: if your code only ever calls add(e) and then iterates, ArrayList is the better default. If you find yourself calling addFirst or addLast frequently, consider whether a Deque implementation better matches your intent. The choice is not about which is "better" in absolute terms, but about the access patterns your application actually exercises.

java linkedlist add: Practical Usage and Code Examples | RYUSLOG DEV