There are many points in your question, I'll try to address them all:
List Implementation
Java actually has many implementations of the List
interface. It is important to pick the right implementation for your use case. Some lists implement. Some List
implementations also implement interfaces like Deque
or Queue
or RandomAccess
and provide extra useful methods.
If the expected length of your list is large, it is also important to pick the implementation with the right performance characteristics for your usage patterns. Some implementations are better at dealing with random reads, random inserts, etc... If your lists are small though, you probably won't notice any performance difference.
The most common implementations are:
ArrayList
All the elements in the list are placed in a backing array. This is nice because it offers really fast random read access to elements. The complexity ArrayList::get(index)
is O(1) (doesn't depend on the size of list). It is also fast to iterate over in both directions.
However ArrayList
has a pretty bad random insert/delete performance. Adding or removing elements at the end of the list is a fast O(1) operation, but inserting or deleting anywhere else in the list is a much slower O(n), because all the elements in the array placed after the insertion point must be shifted right/left. The worst case for ArrayList
is modifiying the beginning of the list.
Removing elements one by one while iterating using an Iterator
is pretty bad: O(m*n) where n is the length of the list and m is the number of elements you are removing.
LinkedList
The java LinkedList
is actually a doubly linked list. It has a bad random access performance O(n), but is fast to iterate over. Adding or removing items at the head or the tail of the list is a very fast O(1), but adding elements at random positions in the middle of the list is O(n), because the implementation must first follow all the links to find the insertion point before doing the insert. If you are going to insert many elements over a small random range, you can however gain a lot of performance by it on a sublist()
, so that the number of links that need to be followed is reduced to the size of the sublist.
Removing elements one by one while iterating using an Iterator
is o(m) where m is the number of elements you are removing (note that the length of the list is not a factor.
insertBefore, insertAfter
These operations described on the wikipedia page you linked are actually operations that are internal to any linked list, and are usually not part of the public API of the list. As you can see, those operations are described using Node
instances, which are an internal implementation detail of a typical linked list.
In Java, you can use the various flavors of add()
or addAll()
and combine it with indexOf()
or sublist()
to achieve the same result and performance characteristics.
Iteration from an arbitrary point
In both cases, it is trivial from a code point of view to iterate on the list starting from an arbitrary point. You can use the listIterator()
method to obtain an iterator that can iterate from a specific point in any direction.
You can also use the sublist(start, end)
method, which returns a new List
instance that is a view on the underlying list (ie: if you modify the list returned by subList()
, it actually modifies the original list). So to iterate over elements 2-5, just do myList.sublist(2, 6).forEach(el -> doSomething(el))