Deleting a node from a linked list LUT? - java

We are trying to write a method to delete a node with a particular key from a linked list implementation of a LUT.
Summary of the code I wrote:
public void delete (String k) {
Node currNode = listHead;
Node prevNode = listHead;
Key key = new Key (k);
while (!currNode.key.equals(k) && currNode != null){
prevNode = currNode;
currNode = currNode.next;
}
if (currNode == listHead) {
listHead = listHead.next;
} else {
prevNode.next = currNode.next;
}
}
My friend wrote essentially the same thing but didn't use a previous node pointer, and instead wrote as his last line:
currNode = currNode.next //detach point, override
Are both of these equivalent? I think what I'm confused about it Java memory management.
If you have already created your listHead Node somewhere else, and you write:
Node currNode = listHead;
currNode is only storing a reference to the memory location where listHead is stored, right? So in the while-loop when you do currNode = currNode.next, what you're doing is going to the memory location referenced in currNode and looking at the variable next and storing the reference to that memory location in currNode? So basically updating where currNode is pointing to. This would mean that my friend's code was wrong , right? Since his code would similarly mean: "update the reference currently in currNode with the memory location of currNode.next".
Would someone mind helping me remove the fog please?

You friend's can't be right because one has to change the .next field of a Node to delete a Node from the list.
As I imagine you know, to delete node N from a list you need set the .next field of node N-1 to refer to node N+1. Your friend's approach can't possibly be doing that because it's not changing any node's .next field.
As for memory, once N-1's .next field refers to N+1, then N is no longer being kept alive by the list. Whether or not it is eligible for garbage collection depends on if anything else in the program has a reference to it. But the list has now washed its hands of it.

You've already got the correct answer to your query, but you have a couple bugs in your code that won't fit into a comment.
NullPointerException
Your while loop condition is backwards. It should be
while (currNode != null && !currNode.key.equals(k)) { ... }
to avoid a NullPointerException when you reach the end of the list or if the list has no nodes to begin with.
Value not found
The method doesn't handle the case where k is not contained in the list. You need to check currNode for null after the while loop.
if (currNode != null) {
if (currNode == listHead)
listHead = listHead.next;
else
prevNode.next = currNode.next;
}

Your analysis is correct -- your friend's code won't actually remove the node from the list.
I think stepping through your code with a debugger might help clear the fog. If you're not already using an Integrated Development Environment (IDE), I'd recommend IntelliJ IDEA, Eclipse, or Netbeans -- all of them include a debugger.

Related

Why does insertion work in linked list when I put node!= null

I am trying to insert a node to the end of a linked list. I noticed that when I put node.next != null in the while loop it works but not when I put node!= null. Can you please tell me why is it like that?
This works
while(node.next!=null){
node = node.next;
}
node.next = new Node(int 5);
This wont work.
while(node!=null){
node = node.next;
}
node = new Node(int 5);
Because when node.next is null it does not mean that it is pointing to some memory zone which has no object, but it means that is not pointing to anything.
So when you assign null to node you are not in some memory being pointed by someone, but you are literally nothing and nothing is pointing to you.

Why we can use Node head = null without instantiate the 'head' in Java?

I am reading someone's code. It is about getting input numbers and convert those number into a Linked list. The part of the code is like this:
class Node {
int value;
Node next;
Node() {
next = null;
}
}
Firstly We need to create a head node to indicate head and we let the head be null like this Node head = null.
My limited experiences of java tell me that head is supposed to be a Node type object here. So why we can use Node head = null without instantiate the head?
I think at least I should create Node head = new Node(); then we can use Node head = null;
Anyone can explain it to me?
Node head = null;
This line states that there are no items in the linked list. This is valid in Java and indicates that although head can contain a Node object (or an object of a derived class), it is currently empty.
To add an item to the list, there is likely some code such as:
public void addItemToLinkedList(int value) {
if (head == null) {
head = new Node();
head.value = value;
} else {
...
}
}
So if there is no first Node (when head equals null) then head is created. Otherwise if head already exists, the else block would execute which would look for the end of the list and add another Node.
head is supposed to be a Node type object here
This is optional. Java allows head to be a Node object, or null, to indicate that head is not referencing any nodes at all.
The value of null is special in Java. It is allowed to go wherever an object can go. It indicates that the variable to which you assign null is empty. This is perfectly fine, assuming that the rest of your program deals with null checking.

Delete an element from a linked list with constraints

This is a fairly easy question but I'm confused:
Given a Singly Linked List, write a function to delete a given node.
1) It must accept pointer to the start node as first parameter and node to be deleted as second parameter i.e., pointer to head node is not global.
2) It should not return pointer to the head node.
3) It should not accept pointer to pointer to head node.
The solution in Java is as following:
void deleteNode(Node node, Node n) {
if (node == n) {
if (node.next == null) {
System.out.println("There is only one node. The list "
+ "can't be made empty ");
return;
}
node.data = node.next.data;
n = node.next;
node.next = node.next.next;
System.gc();
return;
}
// When not first node, follow the normal deletion process
// find the previous node
Node prev = node;
while (prev.next != null && prev.next != n) {
prev = prev.next;
}
if (prev.next == null) {
System.out.println("Given node is not present in Linked List");
return;
}
prev.next = prev.next.next;
System.gc();
return;
}
I'm confused about why in deleting the head node, we're not modifying the head pointer but copying the fields instead (changing the content), but in deleting other nodes, it's simply prev.next = prev.next.next
Does it work if we just do head = head.next instead when deleting head node?
Thank you!
The reason the code copies the data rather than modifying the variable referencing the head is that other users of the list will have a reference to the head node. Changing the local variable will have no effect on their references so you won't have actually deleted the node. Copying the data to the head node effectively removes it.
So, for example, if you had code that did the following:
Node head = new Node("A");
Node tail = new Node("B");
head.next = tail;
deleteNode(head, head);
Then you would expect head.data to be "B" because the original node has been deleted. If you merely do node = node.next then head will still point to the original deleted node.
There are quite a few issues with the code you've posted so please add a comment if you want suggestions on improvements that should be made. It is not a typical algorithm for deleting nodes from a linked list.
One clear issue you've asked about is the use of System.gc. It is not necessary. There are rare cases when Java code needs to take explicit control of garbage collection. This isn't one of them. There's a good explanation of this in the accepted answer to this question.
You asked in the comments why deleting the head requires moving data while deleting other nodes only requires redirection around the node. The reason is because you don't have access to references to the head (as explained in the answer above). You do have access to references to other nodes (i.e. the previous node's next) so they can be changed directly rather than having to copy data.
For your reference, a much more standard implementation is to have the list itself store a reference to the head. Then the copying of node data can be completely avoided. Also note this compares to a value because the node class is private.
static class LinkedList<T> {
private class Node {
private final T value;
private Node next = null;
public Node(T value) {
this.value = value;
}
}
private Node head = null;
public void add(T value) {
Node node = new Node(value);
node.next = head;
head = node;
}
public void remove(T value) {
while (head != null && head.value.equals(value))
head = head.next;
Node prev = head;
while (prev != null && prev.next != null) {
if (prev.next.value.equals(value))
prev.next = prev.next.next;
else
prev = prev.next;
}
}
}
This avoids the arbitrary restrictions in the example you provided such as not being able to delete the head if it's the only node.

Implementation of removeFirst() method in SLinkedList in java

I got the following code from one book for implementing a singly linked list. And I don't understand some lines of code in the removeFirst() method, which removes the first node from the LinkedList.
class ListNode{
private String element;
private ListNode next;
public ListNode(){
element = null;
next = null;
}
public ListNode(String s, ListNode n){
element = s;
next = n;
}
//Access method
public String getElement(){
return element;
}
public ListNode getNext(){
return next;
}
//Modify method
public void setNext(ListNode n){
next = n;
}
}
public String removeFirst(){
if(head == null)
return null;
else{
ListNode temp = head;
head = head.getNext();
temp.setNext(null); //Which I don't understand, is it necessary?
size --;
return temp.getElement();
}
}
It seems that the statement temp.setNext(null); can be omitted. So why it is here, does it has anything to do with the garbage colletion in java. Since I am new to Java, any suggestions or ideas?
It depends on the entire implementation of the linked list, which you have not included in your question. However if it is possible for objects to hold a reference to a node even after if has been removed from the list, then the line is necessary.
Suppose we have a long chain of nodes A -> B -> C -> .... Suppose all of these nodes have been removed from the list, but that we still hold onto a reference to A. If all the nodes still held a reference to the next, this would prevent all of the nodes from being garbage collected. Simply setting the next node to be null ensures that only A cannot be garbage collected.
It is likely that implementations of a linked list do mean that references to nodes can be retained. For example, many implementations of Iterator hold a reference to the current node.
Consider this code:
Iterator<String> iterator = list.iterator();
while (i.hasNext()) {
if ("foo".equals(i.next())) {
i.remove();
break;
}
}
// lots more code
This code searches a list for the first occurrence of the String "foo". If it is found, it removes the "foo" from the list and breaks from the loop. The trouble with this is that the Iterator i is still in scope for the remaining code and still holds a reference to a node. This node may be in the middle of the list if the break occurred. Without setting next to be null, this would prevent all subsequent nodes from being garbage collected while i is still in scope, even if the list is cleared.
Note that you should generally make an iterator local to a loop anyway, like this
for (Iterator<String> i = list.iterator();;i.hasNext())
Let us suppose, single linked list of nodes is : A --> B --> C --> D with head node as A. Now, lets go though your removeFirst() method. When it get called, if(head == null) condition doesnt satisfy because our head node "A" is not NULL. Then it execute else statement,
temp = head //take head reference into temporary variable
head = head.getNext(); //As we want to delete first Node which is head so we are setting head to next node (i.e. head --> next) which is Node "B"
Now, to delete A (first Node) we need to break connection between A (which is previous head)--> B (current head) and that will done by
temp.setNext(null); //so out linked list became A B-->C-->D
and then as one node is deleted so in next statement it decreases size of link by size--.
I think we also set the temp reference to NULL as temp=NULL to make deleted node eligible for garbage collection.

Setting myself to null - Java

I came across the following problem:
Delete a node in the middle of a singly linked list, given only access to that node. (head is not given)
Now there are a lot of solutions and they all do not work when the element to be deleted is the last node.
Why wouldn't this work?
public static void removeNode (Node n){
if(n.next == null){ //n is the last node
n= null;
return;
}
//handling general case here
}
Java passes parameters by value, so setting n to null has no effect outside of the method. This means the method essentially does nothing when passed the last node of a list.
You need to set null the reference in the previous node, not the variable that references to your last node, something like this:
if(n.next == null) {
prev.next = null;
return;
}
n is local to the method, so changing its value won't affect the list itself. You need to modify the next of the previous node, which you do not have access to.

Categories