No, not always - that's why it says "Measure, don't guess".
Anyway apart from being Java, that article is over 10 years old and things change - libraries get optimized (yes, even Java )
I have no idea how the various pieces at play are implemented in Java - so why don't you simply run your own benchmark?
I explicitly did not mention search but wrote insert or delete. I am well aware of the fact that access to contiguous memory is so much faster than random memory access that I know that if you add searching to the use case the more costly insert/delete can become totally irrelevant simply because the search is so much faster. Of course, usually, you don't just insert or delete to a linked list but also perform some search to determine the location where to insert or delete which is the reason why many benchmarks also include search.
I know a lot of literature mentions using linked list + hashtable but I would probably simply use a queue or deque that is backed by a circular array buffer.
If there is anything to take away from the article you linked to then it is this:
This is because it completely ignores constant factors. Constant factors are why often the fastest algorithms and data structures, in reality, are hybrids that combine different algorithms. IntroSort is a good example that combines QuickSort with InsertionSort (for small sizes) and switches to HeapSort for QuickSorts worse cases. If you would ask "Which is better: QuickSort or InsertionSort?" Many people would say "QuickSort" but the the fact is that it depends on the number of elements. Similar to the question "Which is faster: a hashtable or linear search in an array?" Again it depends on several factors: the number of elements, the expense of comparison and hashing, and the actual implementation of the hashtable.
And talking about the O(n) for inserting or deleting from a list. Here is a little benchmark and the results from Delphi 10.4 and 11.3:
program BenchListInsertDelete;
{$APPTYPE CONSOLE}
uses
Spring.Benchmark,
System.Generics.Collections;
procedure InsertDelete(const state: TState);
var
list: TList<Integer>;
i: Integer;
_: TState.TValue;
begin
list := TList<Integer>.Create;
list.Capacity := 1000;
for _ in state do
begin
for i := 1 to 1000 do
list.Insert(0, i);
for i := 1 to 1000 do
list.Delete(0);
end;
end;
begin
Benchmark(InsertDelete, 'insert-delete').MinTime(2);
Benchmark_Main();
Readln;
end.
Delphi 10.4.2
----------------------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------------------
insert-delete/minTime:2,000 223341 ns 223644 ns 11947
Delphi 11.3
----------------------------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------------------------
insert-delete/minTime:2,000 41379 ns 41487 ns 68923
You can guess why that is