A good part is already answered in What is the difference between simply-chained and double-chained list?.
So chained list is the same as linked list (Linked list).
The functioning of the chained list is through independent nodes where has the element. Each element has at least one pointer indicating where the next node is. In the double chained list there is a pointer indicating the previous node. This is precisely why you only know where each node is if you know where the previous one is or the next one, it can only be accessed sequentially. But being independent makes it easy to put what you want where you want in the middle of the list.
Usefulness
It is useful when you need to manipulate your elements internally, i.e., you need to include and/or remove items in the middle of the list simply. But you have to do this very often, if you’re low, you don’t usually make up for it (you always have an exception). Its main feature is to be O(1) for these operations. But it has O(n) for access which is the most commonly used operation in most situations. So she’s pretty little used.
It has the advantage of having its elements scattered, and so it’s easy to manipulate the list, but it has the problem of not being a contiguous sequence, so it can’t randomly access, to get to the element it needs to do a sequential search. That still has the problem that it is not cache friendly and the processor may have to work harder to access the elements.
In fact, in most cases where there is a need for internal manipulation of the list, it is more interesting to use a binary tree that allows inserting and removing elements in O(log n) and accessing the elements in O(log n). The complexity O(log n) is very close to O(1). So today it is a very little used structure, the problems are rare where the need to manipulate internally and the access only needs to be sequential. In some cases Radix can be more useful.
A common error is that insertion and removal may occur on O(1) always. The operation alone can always occur on O(1), but you need to be at the node you want to insert or remove, otherwise you will have an additional O(n) until you get to it.
See about Big O. Remembering that these measures are always the worst case. In linked list the average case is O(n/2) and the best case O(1), if it is the first (last) item.
Examples
It is very common for languages to have a chained list implementation in their standard library. In C# in general you do not need to make one , already exists the LinkedList
(see the source code) that takes good care of almost every situation. I only see why someone should do it on their own if they are studying the subject, if they need something very specific (very rare), for example if they think that the cost of having two pointers is too high to use it, then a simply linked list may be more useful (rare).
There is still the use of circular chained list, but it is very rare to need. It has no beginning or end, when it comes to the end, it already points to the beginning. This link of Wikipedia has a more detailed description on the whole subject.
A practical example is the filesystem (has nothing to do with RAM) and the virtual memory management.
A simple and very naïve implementation and without being DRY, many methods are missing that may be useful:
using static System.Console;
public class Node<T> {
public T Value { get; set; }
public Node<T> Next { get; set; }
}
public class LinkedList<T> {
private Node<T> head = null;
public Node<T> Add(T value) {
var node = new Node<T> {Value = value};
if (head == null) head = node;
else {
var current = head;
while (current.Next != null) current = current.Next;
current.Next = node;
}
return node;
}
public T Remove(Node<T> node) {
if (head == null) return node.Value;
if (head == node) {
head = head.Next;
node.Next = null;
return node.Value;
}
var current = head;
while (current.Next != null) {
if (current.Next == node) {
current.Next = node.Next;
return node.Value;
}
current = current.Next;
}
return node.Value;
}
public void Print() {
var current = head;
while (current != null) {
WriteLine(current.Value);
current = current.Next;
}
}
}
public class Program {
public static void Main(string[] args) {
var ll = new LinkedList<int>();
var node1 = ll.Add(1);
var node2 = ll.Add(2);
var node3 = ll.Add(3);
var node4 = ll.Add(4);
var node5 = ll.Add(5);
ll.Print();
WriteLine();
ll.Remove(node3);
ll.Print();
}
}
Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.