If we are implementing a LRU cache using HashMap and DoublyLinkedList, What is the best way to implement evict() method with O(1) time complexity?
LinkedList from Java didn't expose the Node type (which is a private static inner class).
So you can't remove it in in O(1), because a sequential scan is required.
To get O(1), you need to be able to access the Node type, so that could remove it without scan.
You have to write it by yourself. Fortunately, a doubly linked list is relatively easy to write, and it's a pretty beneficial & fun task to do.
How to remove with a given Node?
Refer to this answer: https://stackoverflow.com/a/54593530
The method LinkedList.java -> removeNode() remove a given node, without sequential scan.
The code in this answer is for a singly linked list, the remove for a doubly linked list is even simpler in some case.
Tips:
If the given node is the end node in linked list, then you need the previous node too.
But that's for singly linked list, for a doubly linked node, the node itself contains the previous node, so you don't have to pass previous node to the removeNode() method.
BTW
Why it's beneficial?
linked list is the most basic structure (except array and bits), that some other very basic structures could built base on.
e.g both queue and stack could be implemented easily with a linked list.
Concurrent access
java.util.LinkedList is not thread-safe, your LRU might needs some concurrent control, but I'm not sure.
If need, then java.util.concurrent.ConcurrentLinkedDeque is a good example to refer to.
#Update LinkedHashMap
java.util.LinkedHashMap, is a combination of hashtable & doubly linked list.
Mechanism:
It extends HashMap to get the O(1) complexity for the common operations.
And use doubly linked list to keep track of insertion order.
head is the eldest item, and tail is the newest item.
It can be used to impl some kind of cache, though I am not sure will it be fully qualified for your requirement.
Related
My aim is to delete a node somewhere in the middle of a Java LinkedList object, in O(1) time.
If I can get a reference to the node, I could probably do this myself without the need for a Java-provided method. But I cannot seem to find a way to get a reference to anything but the head of the list.
How can I get a reference to the last node in a Java LinkedList object? I wold then store these references in a map to use later.
Note: I know this is doable if I implement my own LinkedList, but is there a way to do it with Java's LinkedList class?
I would suggest actually changing your data structure to LinkedHashSet here instead of LinkedList. The reason for this is that LinkedHashSet#get() and remove() can lookup or delete any element by key in O(1) time. Also, a LinkedHashSet is implemented with a linked list running through the entries. The order of the entries while iterating the list are determined by the insertion order, so it behaves similarly to a LinkedList in that regard.
The closest thing you can get to a direct reference is an Iterator that points to a specific point.
If you call remove() on such an Iterator it should work in O(1):
LinkedList<Object> linkedList = ...;
Iterator<Object> it = linkedList.iterator();
while (it.hasNext()) {
if (matchesSomeCondition(it.next()) {
it.remove();
}
}
Note that this sample code definitely doesn't run in O(1), it's specifically just the remove() call that can have that efficiency. If you haven't already identified the node in some way (such as positioning an Iterator at that place), then you won't be able to remove an element from a LinkedList in O(1) time.
Edit and since you mention "last recently added" element then maybe a ListIterator is the thing to use, since it has an add() method. If you can efficiently implement all your adding/removing using a ListIterator, then you can keep the traversal operations over the LinkedList to a minimum. In fact, if you always use indexes to add/remove objects from your LinkedList then you loose a lot of its efficiency (since each add/remove call has to find the affected item first via traversal).
Lots of classes in Java are prefixed with the "Linked" identifier, i.e. LinkedList, LinkedBlockingQueue, LinkedHashmap, etc. What does the term "linked" mean?
A Java LinkedList, is a List implementation that uses linked lists. In contrast, one could also implement them using for instance dynamic arrays, which is what ArrayList does.
A LinkedBlockingQueue follows much the same idea as a LinkedList.
A LinkedHashMap, is a normal hash table (which provides efficient random access), combined with a doubly-linked list (which provides consistent iteration order).
Thus, the Linked prefix means, that a linked structure (such as singly- or doubly-linked lists) are a key part of their underlying implementation.
Here, the term "linked" means that each member of the collection is aware of the next member in the collection via a "link"; therefore, each member can be stored in a non-sequential location in memory.
The above, very simple contribution is derived from the Linked List Wikipedia article mentioned in a comment made on the original question.
There are some Collection in java which starts with Link. Regardless what is followed by Link there is some common property of these collections -
1. These collections are always ordered
2. Can insert element at any position. For example you can insert item at a LinkdeList.
3. mantains a link to connect with the next/previous item where each item is called node. You ma consider a simplified version of node like this -
class Node{
int value;
Node next;
Node previous;
}
Here Node next and Node previous works as a link to the next/previous node from the current node.
I have an algorithm where I pass through nodes in a graph in a certain way, occasionally passing through the same node several times, and I need to form a list of the nodes passed, such that a node appears once for the last time I passed it.
For instance, if I passed through nodes A -> B -> C -> A -> C, the list I need in the end is [B, A, C].
What I wanted to do was to use a LinkedList, such that every node in the graph will contain a reference to its node in the LinkedList. Then, every time I pass through a node, I will remove its corresponding node from the LinkedList and insert it again into the end of the LinkedList, and the complexity of the operation will only be O(1).
However, when I began implementing this, I ran into a problem: apparently, the java class LinkedList does not allow me to see its actual list nodes. Using the regular remove functions of LinkedList to remove the list node containing a given graph node will be O(n) instead O(1), negating the whole point of using a LinkedList to begin with.
Naturally, I can implement LinkedList myself, but I would rather avoid that - it seems to me that if I have to implement LinkedList in java, I'm doing something wrong.
So, is there a way to solve this problem without implementing LinkedList on my own? Is there something that I'm missing?
As it seems, you are expecting a built-in approach, i don't think there is any Collection which provides such functionality. You will have to implement it on your own as #MartijinCourteaux suggested. Or:
use Sorted Set collection like TreeSet<E> with supporting cost of O(log n) for operations: add, remove and contains.
LinkedHashSet<E> But beware unlike HashSet<E>, LinkedHashSet can have O(1) expected performance for operations: add, contains, remove but the performance is likely to be just slightly below that of HashSet, due to the added expense of maintaining the linked list. But we can use it without incurring the increased cost associated with TreeSet. However, insertion order is not affected if an element is re-inserted into the set so try removing the first insertion of an element before re-inserting it.
LinkedHashMap keeps order of entered values and allows remove node by its key and then put back to the end. I think that it is all you need.
Unless your linked list is large just using a regular array list will give fast performance even with the shuffling. You should also consider using hash sets, if order is not important, linked hash set if the order of insert matters, or tree set if you want it sorted. They don't allow duplicate values but have good O performance for insert, delete and contains.
In the implementation of HashMap, linked lists are used to represent elements in buckets.
Each Entry has a element to the next Entry. See: Ref. However, in the implementation for the LinkedList class, each element has a reference to its previous element and its next element see Ref. Just trying to figure out why previous is important in one linked list and not another?
Entry (internal class of HashMap) is not a part of general-use linked list (as LinkedList is). It's sole purpose is to iterate over it in forward direction looking for an element. So it does not need a previous reference.
the previous reference make the LinkedList a bidirectional List,this makes it possible to reversely iterate on a List .
The reference to the previous element is not needed in a linked list, stricly speaking. The java.util.LinkedList is actually a doubly-linked list. This is needed for an efficient implementation of the following operations:
add(E), which append at the end of the list;
getLast(), which retrieves the last element of the list;
ListIterator.previous() which allow traversal of the list in reverse order.
Said operations are of no use for the linked list of Map.Entry.
Note that while getLast() is a LinkedList adition to the list interface, the two other are required by the said interface.
The LinkedList is a general-purpose implementation. You may want to iterate over it backwards. For Maps, when searching a bucket it only iterates forward. Since there is not need to iterate backward, it is not implemented.
Let's say the List b is a LinkedList.
Let's say the List a is also a LinkedList.
Question:
How do I append these list in constant time?
It is possible, because LinkedList is presumably a doubly linked list (otherwise it couldn't implement the Deque interface). And appending doubly linked list is a 0(1) operation.
The addAll method doesn't run in constant time.
Question:
How do I transform a LinkedHashSet into a list in constant time?
It is also presumably possible because LinkedHashSet "maintains a doubly-linked list running through all of its entries".
Your assumptions are based on no encapsulation - i.e. that the LinkedHashSet is willing to expose its internal LinkedList to the outside world, when I suspect it isn't.
Likewise joining two linked lists - I don't know offhand whether each node knows which list it's in, but that's certainly a possibility which would scupper your constant-time appending. Even if they don't, as soon as you attach the head of one list to the tail of the other, you end up with problems - you've got two lists both referring to the same data, which would have some odd consequences.
In other words, both of these operations are feasible in a computer science sense, and you could build your own implementations to support them, but that doesn't mean the Java API exposes its internals in a way which enables those operations.
You would need to implement your own classes. The LinkedList class does not expose its internal node structure, so you can't just point its last node to the first node of another LinkedList.
The answer is similar for the LinkedHashSet: While it does maintain this doubly-linked list, you don't get to access it.
You do not get access to it but I suspect that Collections does, so you should not give up hope that this is a viable and quick solution to your problem.
I looked further and you are right. If you have Set<Whatever> whatever = SOME CONSTRUCTOR then you can code List<Whatever> list = new LinkedList(whatever); because a LinkedList has a Collections constructor and Set has a Collections interface.