Environment:
java :java version "1.8.0_201"
os:Ubuntu 16.04.6 LTS Linux version 4.15.0-91-generic
Recently I read the source code of java.util.concurrent.ConcurrentLinkedQueue#offer, and I am confused with the code below.
public boolean offer(E e){
checkNotNull(e);
final Node<E> newNode = new Node<E>(e);
for (Node<E> t = tail, p = t; ; ) {
Node<E> q = p.next;
if (q == null) {
// p is last node
if (p.casNext(null, newNode)) {
......
when ConcurrentLinkedQueue is initialized, the item of head and tail is null.
public ConcurrentLinkedQueue() {
head = tail = new Node<E>(null);
}
but after I first invoke ConcurrentLinkedQueue#offer(with queue.offer(1)) and the code executed the line
p.casNext(null, newNode)(here p and head are the same reference), the reference of head was changed to newNode,and 'item' value of head was change to 1.
Detail of p.casNext is like this
boolean casNext(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
It seems only next filed of head was modified in the cas method, but why was the reference of head changed?
Can you give me some explation? Thanks in advance!
It seems only next field of head was modified, how is the reference of head changed?
Correct, head.next was changed.
The head field of the queue is intentionally not modified by the offer(e) method.
The head and tail fields of the queue are never null, so when the queue is empty, they both refer to the same node, and that node has item = null. It is always valid for one or more nodes to have item = null. Those nodes will be skipped when querying or polling the queue.
It's all done that way to make the code thread-safe without the use of locking.
Related
Below is a program to reverse a doubly linked list in gfg.
When I try to submit, I get the following error
Your program took more time than expected. Time Limit Exceeded
Expected Time Limit 0.00sec.
Can anyone tell me how to decrease my program's time?
Code
public static Node reverseDLL(Node head)
{
Node p,cur;
p=head;
cur=head.next;
while(cur!=null)
{
if(cur.next==null)
{
p.next=cur.next;
cur.next=head;
head.prev=cur;
head=cur;
break;
}
p.next=cur.next;
cur.next.prev=p;
cur.next=head;
head.prev=cur;
head=cur;
cur=p.next;
}
return head;
}
Your code is not correct:
The prev member of the (original) tail node is not set correctly. It is not set in the if block, but in the previous iteration, where it got its value with cur.next.prev=p, which is setting it to the (original) head node. This creates an infinite loop in the data structure. In the end, none of the prev links is null. It might be that the testing framework keeps following those prev links in circles until a time out happens.
Also, the function assumes that head is not null. This may not be guaranteed.
There are also too many assignments happening. With a doubly linked list it is quite simple: just swap the value of next and prev in every node:
public static Node reverseDLL(Node head)
{
if (head == null) {
return null;
}
Node next = head.next;
while (next != null) {
head.next = head.prev;
head.prev = next;
head = next;
next = head.next;
}
head.next = head.prev;
head.prev = null;
return head;
}
this is classic solution for O(N) time, so I guess time constraint for this task is required to. solve it for O(1).
Take a look for this thread: https://www.google.com/url?sa=t&source=web&rct=j&url=https://discuss.codechef.com/t/reverse-a-doubly-linked-list-in-o-1/72850&ved=2ahUKEwjipdrQw_T1AhVYLTQIHahIDx8QFnoECBQQAQ&usg=AOvVaw1c0BDUotM0suEK7I4B9pQs
I need help understanding this. I know how to implement the queue but there is one small part bothering me. I drew on my notebook the flow of how things work but I don't get how the head has a nextNode without me setting it. How does the head end up pointing to the next node?
public void enqueue(T data){
count++;
Node<T> current = tail;
tail = new Node<>(data);
if(isEmpty()) {
///////////////////////////////////////////////////////////////////////////////
// when this runs, doesn't head.getNextNode point to null?
// if the list is empty, then tail is null.
// On the deque method, I can sout head.getNextNode() and I get data back, how?
///////////////////////////////////////////////////////////////////////////////
head = tail;
} else {
current.setNextNode(tail);
}
}
Below, the dequeing works fine, I think I'm having an issue understanding the whole reference/pointer thing
public T dequeue() {
if(isEmpty()) {
return null;
}
count--;
T dataToRemove = head.getData();
/////////////////////-+{[UPDATE]}+-////////////////////////////////
// WHERE DOES HEAD GET THE NEXT NODE FROM? THIS WORKS, BUT WHERE IS
// THE NEXT NODE COMING FROM IS WHAT I'M ASKING?
///////////////////////////////////////////////////////////////////
head = head.getNextNode();
return dataToRemove;
}
I figured it out:
When the list is empty, the head points to tail
then, when the enqueue method gets called again,
current will be pointing to tail, but head will still be pointing to tail reference
so now head is pointing to the same reference as current
in the else statement, current sets next node to the same reference the
head is pointing to. That's how the head gets its nextNode set.
When the method runs again, current will
point to another reference again but head will still be pointing to its original reference. BAM
public void enqueue(T data){
count++;
Node<T> current = tail;
tail = new Node<>(data);
if(isEmpty()) {
head = tail;
} else {
current.setNextNode(tail);
}
}
I tried implementing the insert method for circular linked list. I think I had some success.
Problem:
When I display the list. The display method will loop because every next variable of the link is linked to a non-null node object. So head will never be a null object. From what I recall about singly linked list, head always point to the first node in the list or the first node with data inside of it.
My conceptual understanding of circular linked list:
From what I can understand circular linked is somewhat like a singly linked list but with a slight twist: the next variable of the tail object points to the head.
I'm coding it like the diagram has presented provided by the source link.
Source: http://sourcecodemania.com/circular-linked-lists/
public void insert(String data)
{
Link link = new Link(data);
if(head == null)
{
head = link;
tail= link;
}
else
{
tail.next = link;
tail = link;
tail.next = head;
}
}
public void display()
{
// good implementation for display #2
while(head != null)
{
// System.out.println (head.data);
head = head.next;
}
}
Once you insert at least one element, you would never come across null. It will keep on going till infinity.
Also, it might not be a good idea to modify head just for displaying the list. Operation like display should not have any side effects.
In stead, keep a member field size in your list class and update it in each insert and delete method.
Now you would know how many times you should iterate the loop.
ListClassName current = head; // Head is not modified.
for (int i = 0; i < this.size; i++) {
// System.out.println (current.data);
current = current.next;
}
Good luck.
You can keep a reference to the first Link object and check to make sure head is not equal to this object while looping:
public void display()
{
boolean first=true;
Link firstItem=null;
// good implementation for display #2
while(head != null && head!= firstItem)
{
if(first){
firstItem=head;
first=false;
}
// System.out.println (head.data);
head = head.next;
}
}
Example input: the node ācā from the linked list a->b->c->d->e Result: nothing is returned, but the new linked list looks like a->b->d->e
I do understand that ppl have already asked this question before, but since my reputation is not high enough yet, I couldn't ask my question in that thread. so here goes my quesetion:
So in the solution, when deleting the middle node we do:
public static boolean deleteNode(LinkedListNode n) {
if (n == null || n.next == null) {
return false; // Failure
}
LinkedListNode next = n.next;
n.data = next.data;
n.next = next.next;
return true;
}
But what I don't understand is that why can't I just do n = next?
It is probably a trivial question, but I didn't seem to find a good explanation for this question
If you just do n = next then you have only changed what object your local reference variable n refers to; you haven't modified any part of the list.
The trick to "deleting" the current node is to overwrite it with the next one:
n.data = next.data;
n.next = next.next;
Now you are modifying fields of the object that is referred to by n, which is a part of the actual list.
In C++, the code you wrote would look like this:
bool deleteNode(LinkedListNode* n) {
if (n == null || (*n).next == null) {
return false; // Failure
}
LinkedListNode* next = (*n).next;
(*n).data = (*next).data;
(*n).next = (*next).next;
return true;
}
So what does that mean? When you call this method, in C++ it would look like this:
LinkedListNode* listNode = new LinkedListNode();
deleteNode(&listNode);
This is important, because that means you're just sending an address over, and not the entire object. This means that you don't actually have access to the node you gave as a parameter to the method, you only have a reference to its address.
Basically, in Java, you can't do the following C++ code:
*n = *next;
You can't modify the listNode object that's outside of the method. You only get its address. And you are only modifying the copy of its address, not the address itself.
Basically, it's because in Java, the pointer of the class is passed by value (as a copy), and primitives are also passed by value (as a copy).
somehow, it overwrites the current node which is supposed to be deleted with the
data of next node to it,and delete the next node.
LinkedListNode next = n.next;
n.data = next.data;
n.next = next.next;
That is how the code comes.
http://www.java2s.com/Open-Source/Java-Open-Source-Library/7-JDK/java/java/util/concurrent/ConcurrentLinkedQueue.java.htm
The above is the source code of ConcurrentLinkedQueue.
I am not able to understand one condition.
How the condition (p == q) will come in the below snippet code from offer method
public boolean offer(E e) {
checkNotNull(e);
final Node<E> newNode = new Node<E>(e);
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
// p is last node
if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
if (p != t) // hop two nodes at a time
casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
}
else if (p == q)
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet.
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}
and also what does the author mean by "We have fallen off List"
The ConcurrentLinkedQueue allows concurrent modification of the internal list while traversing it. This implies that the node you are looking at could have been removed concurrently. To detect such situations the next pointer of a removed node is changed to point to itself. Look at updateHead (L302) for details.
The condition asks the question "Is the current node the same as the next node?"
If so, you've fallen off list ( documentation in line. )
The basic outline of steps is:
create a new node for the offered data.
walk the list to find the last node
insert new node as new tail.
The other parts of the if statement are handling concurrent modification issues.
To better understand what's going on, read Node.casTail() and the casNext()