Linked Lists and Their Operations
An abstract data type is a set of possible values and a set of operations on those values, specified without saying how the values are represented or how the operations are implemented.
Try it: Linked List
How a singly linked list stores values in separate nodes joined by next pointers, and how inserting or deleting only rewires pointers.
How it works
- HEAD points at the first node; each node holds a value and a pointer to the next node; the last node points to NULL.
- To insert, create a node, store its value, point it at its successor, then point its predecessor (or HEAD) at it.
- To delete, walk to the node and make its predecessor skip over it.
- Searching means following next pointers from HEAD until the value or NULL is reached.
Default run (9 steps): HEAD → 10 → 20 → 30 → NULL. 3 of 10 nodes. Choose an operation. Example: append 40. … Done: HEAD → 10 → 20 → 30 → 40 → NULL.
Loading the simulation…
Starting With the Question of What a List Is
Suppose you are writing a program that must keep a list of names in alphabetical order. You need to add names, remove names, and check whether a name is present. A natural first question is where the names should live in memory and how the list should be arranged internally. This article asks a different question first: what exactly is the list, apart from the way it happens to be stored?
The answer leads to a central idea in software engineering, the abstract data type, usually written ADT. Once you see what an ADT is, you will also see why the same list of names can be stored in very different ways without changing the rest of the program. Linked lists are one of those ways, and they are the subject of the rest of this article.
Abstract Data Types: Values and Operations
An abstract data type is a set of possible values and a set of operations on those values, specified without saying how the values are represented or how the operations are implemented.
The definition has two halves, and both matter. The first half names the values the type can take. The second half names the operations that act on those values. The definition is silent on everything else. It does not say whether the values sit in a chain of linked objects, in a fixed-size block, or somewhere else. It does not say how an operation works internally. It describes only what is possible and what can be done with it.
An ADT answers the question what is the thing and what can I do with it. It deliberately leaves out how the thing is stored and how each operation is carried out.
The Ordered List of Strings as an ADT
Take the ordered list of strings as an example. Any sequence of strings arranged in increasing order is a possible value of this type. The operations include inserting a new string, deleting a string, and finding a string in the list. Notice what is absent from that description: nothing in it mentions arrays, linked objects, or pointers. Those details belong to a later decision, the implementation.
Describing the ordered list of names at the abstract level
Describe the ordered list of names used in the alphabetical roster program, without deciding anything about memory.
- 1Name the values
The possible values are any sequence of names arranged in increasing alphabetical order. An empty sequence is also a possible value of this kind of list.
- 2Name the operations
The operations are insert a name, delete a name, and find whether a name is present.
- 3Check what is missing
The description says nothing about arrays, linked objects, or pointers. Those are implementation choices and do not belong in the ADT.
The ADT is fully described by its values (ordered sequences of strings) and its operations (insert, delete, find), and you could complete this description on paper before writing any code.
One Type With Several Implementations
The source makes a striking observation: the same list could just as easily have been stored in an array or an ArrayList, and the same operations could still have been implemented on it. A linked list is one implementation of the ordered list of strings. An array-based list is another. Both realize the same ADT.
The internal code for those operations would look different in each case. An array-based insert and a linked-list insert do not work the same way on the inside. Yet their interfaces and their logical behavior stay the same. Callers still ask for an insert, and the list still ends up in the same order. This is the core of what makes an ADT useful: one abstract type can have several implementations.
| Aspect | Array-based implementation | Linked-list implementation |
|---|---|---|
| Shape of the stored list | Elements are held in an array or ArrayList | Elements are held in separate nodes tied together by references |
| Inside code for insert | Differs from the linked version internally | Differs from the array version internally |
| Interface callers use | Insert, delete, find | Insert, delete, find |
| Logical behavior seen by callers | List stays in order | List stays in order |
Two implementations of one ADT. The internals differ; the interface and the logical behavior the caller sees do not.
Predicting What a Caller Notices
What do you think happens?
A program only calls insert, delete, and find on its list. If you swap the array-based implementation for a linked-list implementation, does the program's code need to change?
Reveal answer
No, the program can keep working because it depends only on the abstract operations
The source states that a program depending only on the abstract definition can use either implementation interchangeably, and that changing the implementation of an ADT does not affect the program as a whole.
Separating Interface From Implementation
The source describes this idea as data abstraction: separating the interface of a data structure from its implementation. The interface is the set of operations a caller can use, along with their logical behavior. The implementation is the concrete way those operations are carried out. Data abstraction keeps these two apart on purpose.
Think of the calling program as sitting above the interface and depending only on it. The implementations sit beneath the interface, and either one can fill the role. Because the dependency stops at the interface, a change to the storage below it does not reach the program. If a team writes its program against the abstract definition, then switching the storage later is a change confined to the implementation.
Changing the implementation can make a program easier to debug and maintain, because the fault or the improvement is localized to one place rather than spread through the whole program.
Linked Lists as Chains of Nodes
A linked list is implemented as a sequence of nodes, where each node contains data and a reference to the next node.
In a linked list, the list is not stored as one continuous block. Instead, it is built from separate pieces that are tied together. Each piece knows where the next piece is. Once you understand that single idea, most of the behavior of a linked list follows from it.
Read the diagram as a set of arrows. Each arrow starts at one node's reference and lands on the next node. No node in the middle is reached directly from the list as a whole; every node is reached by leaving the node before it. This is the defining feature of the structure and the reason it is called a linked list. The chain has a beginning, which is the first node, and it ends when a node has no next node to point to.
Tracing a Search Through the Chain
To reach an element, a program starts at the first node and follows references one at a time. To find an element in a linked list, a program starts at the first node and examines its data. If that is not the element it wants, it follows the node's reference to the next node and repeats the check. The program never jumps directly to the third element. Each step depends on the reference stored in the node just visited.
Tracing a find operation step by step
The chain holds Ana, then Ben, then Cruz. The program must find Ben. Trace which references it follows.
- 1Visit the first node
The program starts at the first node, which holds Ana. Ana is not Ben, so the search continues.
- 2Follow the first reference
The reference in the Ana node leads to the node holding Ben.
- 3Compare the data
The node holds Ben, which matches the target, so the search can stop.
- 4Stop without visiting Cruz
The program never needs to follow the reference in the Ben node, because the element was already found.
The find operation visits two nodes, follows exactly one reference, and stops when the data matches.
Mistakes When Building a String List
Common mistake Treating the ADT as if it dictates the storage
What it looks likeDeciding that the ordered list of names must be an array because the first implementation happened to use one.
Why it failsThe ADT names only values and operations. Storage is an implementation choice that can change without affecting the program as a whole.
Do this insteadWrite the ADT first, then choose an implementation, and keep the choice behind the interface.
Common mistake Losing the head reference
What it looks likeMaking a new node the first node without first recording where the old first node was, so the rest of the chain can no longer be reached.
Why it failsEvery node is reached by leaving the node before it. If no reference leads into the chain, the remaining nodes cannot be reached by traversal.
Do this insteadBefore changing the first node, make sure the new node's reference points to the old first node, and only then update the reference that identifies the first node.
Common mistake Breaking a link in the middle of the chain
What it looks likeOverwriting the reference in one node so that it no longer points to the node that follows it.
Why it failsTraversal depends on each reference. A broken link cuts off every node after it, even though those nodes still exist.
Do this insteadWhen updating a reference, confirm that the node you are linking to is the one you intend, and that no other node loses its only route to the chain.
Common mistake Confusing the data part with the reference part
What it looks likeReading a node's reference and assuming it is a name stored in the list.
Why it failsThe reference part holds no value. It only tells the program where the next node is.
Do this insteadKeep the two roles separate: the data part is compared with the search target, and the reference part is only followed.
Common mistake Assuming the last node has a next node to follow, instead of recognizing that the chain ends when there is no next node
Common mistake Skipping tests and trusting the list because it looks correct on paper
In the after state, the first-node reference reaches only Dee, and Dee has no reference to Ana. Ana, Ben, and Cruz still exist as objects, but nothing in the list leads to them. The fix is to link Dee to Ana first, and only then move the first-node reference to Dee.
Testing a Linked List of Strings
An implementation is only trustworthy once it has been tested. The source material pairs StringList.java, which implements a linked list of strings, with a separate program, ListDemo.java, whose job is to test the StringList class. Keeping the test in its own program separates the code that builds the list from the code that checks it.
It is worth thinking of a test as a second view of the same chain. You build a list, add or read strings, and confirm that the results match what the chain should contain. If the test finds a missing name after an insert, the likely cause is a broken or missing reference rather than a problem with the names themselves.
A chain holds Ben, then Cruz, then Ana. Trace a find operation for Cruz. List each node you visit, the reference you follow at each step, and the condition that stops the search.
Hints
- Start at the first node and compare its data with the target before following any reference.
- Count how many references you follow before the data matches.
Key Takeaways
- An abstract data type is a set of possible values and a set of operations on those values, specified without saying how they are represented or implemented.
- One ADT, such as the ordered list of strings, can be implemented as a linked list or an array, and a program that depends only on the abstract definition can use either one.
- A linked list is a sequence of nodes, where each node holds data and a reference to the next node; the chain ends when a node has no next node.
- A program finds an element by starting at the first node and following references one at a time, never jumping directly to a later node.
- Common mistakes include losing the first-node reference, breaking links in the middle of the chain, and skipping tests of the implementation.