If I develop the linked list from scratch, I can store a pointer to a Node object inside my business entity class and achieve constant O(1) remove and insertAfter operations. In the java standard library implementation, they are O(n) which can differ tremendously when processing large data sets.
Why don’t they just made the Node class public and encapsulated some details inside it still making the class itself (via interface maybe) accessable? It would make LinkedList more flexible.
Do we have something like FlexibleLinkedList in Apache Commons or Guava?
Advertisement
Answer
ListIterator
Why don’t they just made the Node class public and encapsulated some details inside it still making the class itself (via interface maybe) accessable?
There is no need to.
Why don’t they just made the Node class public and encapsulated some details inside it still making the class itself (via interface maybe) accessable? It would make LinkedList more flexible.
That already exists.
If you want to have the benefits of node-based operations, like:
- Give me the next item, based on current node
- Delete the node I already have, without locating it
- Insert something after my node that I already have
you just have to use a ListIterator
as returned by list.listIterator(). This method is offered by all List
s.
This class encapsulates the logic of knowing the current node in an iteration and offers efficient methods for manipulation that directly use the Node
s, such as:
add
– The element is inserted immediately before the element that would be returned bynext()
set
– Replaces the last element returned bynext()
orprevious()
with the specified elementremove
– Removes from the list the last element that was returned bynext()
orprevious()
While offering methods to control the iteration with next()
and previous()
.
Example
So you could for example change every second element:
LinkedList<Integer> values = new LinkedList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); int i = 0; ListIterator<Integer> iter = values.listIterator(); while (iter.hasNext()) { iter.next(); if (i % 2 == 0) { iter.set(100); } i++; }
Resulting in
[100, 2, 100, 4, 100, 6, 100, 8, 100, 10]
And this code runs in O(n)
, it does not need to re-locate the nodes each time. In contrast to the bad equivalent of
for (int i = 0; i < list.size(); i++) { if (i % 2 == 0) { list.set(i, 100); } }
which runs in O(n^2)
for the reasons you have stated.
Benefits of hiding Node
In general, it is much better to encapsulate and hide your private inner workings. An user should not bother how LinkedList
does its job behind the curtain.
Also, if it would expose the Node
, an user could sneakily edit them and the whole list goes bonkers.
For example an user could then do
Node a = list.getNodeAtIndex(3); Node b = a.next; Node c = b.next; // Remove b a.next = c; c.previous = a;
Without also adjusting the size
of the list. So list.size()
would return a wrong number now, probably leading to crashes during iterations.
Or you could also introduce a dangerous loop:
a.next = b; b.next = a;
Or forget to set previous
, resulting in a different list when iterated backwards:
a.next = c; c.previous = b;
ListIterator
ensures that such stuff can not happen, while offerring the same functionality. So instead of exposing the nodes directly to the user, it exposes only the desired functionality, in form of methods for which it has full control over.