How come when I create a linked list node, append some data, then move the head in another method, the head stays the same in the callee method?
For example:
public static void append(Node n, int d) {
while (n.next != null) {
n = n.next;
}
n.next = new Node(d);
}
public static void test(Node n) {
n = n.next;
}
public static void main(String[] args) {
Node head = new Node(5);
append(head, 4);
append(head, 3);
test(head); //this moves the head to head.next
//why is head still = 5 after we get here?
}
In the method append, the line n = n.next won't affect the original node passed as argument, in your case head. Why? Because Java is pass by value. This means that if inside the method, you modify the reference of head (which is received as n inside the method), it won't affect the original reference. Therefore, head will still refering to the same location in memory (the same object).
Also, your method test doesn't do anything because you are creating a local variable:
Node next = ...;
and then assigning n.next to it. But this variable only exists inside that method, so it won't affect anything outside it.
next is a property, not a method. Your test method is just grabbing the reference to n.next, it is not "moving the head."
Related
After I made my TODOs
I know the solution is as below
public void deleteNode(Node node) {
node.data = (node.next).data;
node.next = (node.next).next;
System.gc();
}
But my question is that
How above solution differ than below another one
Is both are valid solutions and make the same functionality or not
public void deleteNode(Node node) {
node = (node.next);
System.gc();
}
When you do
node = (node.next);
you change parameter, but doesn't change any data.
Java use call by value approach (it slightly difficult to understand what does it mean to pass reference by value).
Here is little example:
void foo(int i) {
i = 2;
System.out.println(i);
}
when we call it
int j = 1;
foo(j);
System.out.println(j);
we get 2 and 1 on console.
It means that when foo is called copy of the value passed to it.
PS:
You don't need to call System.gc();.
Thanks to #talex
And I visualize the cases to be more clear
Lets assume the problem to delete following node c from the Linked List
And we ONLY have access to this node
First solution will actually delete the node
public void deleteNode(Node node) {
node.data = (node.next).data;
node.next = (node.next).next;
System.gc();
}
By copy next cell (value and pointer) to the parameter cell
So the end result
Parameter cell will be considered as deleted and Linked List will be chained correctly
Hints:
No problem that node d not used by any pointer, GC will clean
This solution will NOT work if we need to delete the last cell
Second solution will NOT delete the node
public void deleteNode(Node node) {
node = (node.next);
System.gc();
}
Thats because its ONLY change pointer of the parameter cell
So the end result
Parameter cell will NOT be deleted and Linked List still have the cell
I'm trying to used recursive method to complete the addLast method in a singly linked list, however, the code gives me a wrong output of list.size() = 2 and list.getFirst() = 5. The reason should be due to the line
SLList p=this;
It seems changing p reference changes "this" reference as well, which is not so logic to me. Could anyone give some details about this? Thx
public class SLList {
public class IntNode {
public int item;
public IntNode next;
public IntNode(int i, IntNode n) {
item = i;
next = n;
}
}
private IntNode first;
public SLList(int x) {
first = new IntNode(x, null);
}
/** Adds an item to the front of the list. */
public void addFirst(int x) {
first = new IntNode(x, first);
}
/** Retrieves the front item from the list. */
public int getFirst() {
return first.item;
}
/** Adds an item to the end of the list. */
public void addLast(int x) {
SLList p = this;
if (p.first. next == null) {
p.first.next = new IntNode (x, null);
}
else {
p.first = p.first.next;
p.addLast(x);
}
}
/** Returns the number of items in the list using recursion. */
public int size() {
/* Your Code Here! */
SLList p = this;
if (p.first == null) {
return 0;
}
else if (p.first.next == null){
return 1;
}
else {
p.first = p.first.next;
return 1 + p.size();
}
}
public static void main (String[] args) {
SLList list=new SLList (5);
list.addFirst(10);
list.addFirst(15);
list.addLast(17);
System.out.println(list.getFirst());
System.out.println(list.size());
}
}
The problem is nothing to do with the assignment of this. Nothing can change this. Period.
(But things can change the state of the object that this refers to.)
The real problem is in your implementation of the size method. Your size method is causing the list to change. It shouldn't. In your case, the change causes:
the size() method to return the wrong value
subsequent getFirst() calls to return the wrong value.
I won't say exactly where the bug, but you should be able to spot it yourself by a process of elimination. (Or if that fails, use a debugger and try to observe where the list is changing.)
There are bigger problems with your algorithms than you think. size() is incorrect. You can fix this if you realize that you need to count the number of IntNode objects in the list. Similarly all other methods need to manipulate IntNode objects.
SLList p = this;
p reference to the same SLList object. if you make any changes to 'p' then it will also happened to 'this', becuase of reference type (not value type).
Here in the statement
p.first = p.first.next;
the reference to the first is changed when you call 'addLast' method. You loss the reference to the first item.
If you remove the line
list.addLast(17);
in main method you will see the correct answer. The problem is with this method.
Change the method as follow and add the new method below.
/** Adds an item to the end of the list. */
public void addLast(int x) {
addLast(x, this.first);
}
private void addLast(int x, IntNode node){
if(node.next == null){
node.next = new IntNode (x, null);
}else {
node = node.next;
addLast(x, node);
}
}
Then you will not lose the reference to first item and now it works fine,
Problem in your implementation is addLast and size method are changing the value of field variable first.
It don't matter whether you assignthis to some variable or use directly.
Because assigning this to some variable does not create new this object but assign's reference to that variable.
So you should first copy value of first field variable to some local variable then iterate on it.In this way your first will not change.
Hint: Don't change the first variable reference.
Your addLast() and size() changes value of first which is wrong.
Problem is in this line.
p.first = p.first.next;
I am trying out my own implementation of a double-linked list. While my code is currently functioning, I can't really figure out why. Below is an exerpt of the code:
public class DLList<E> {
public class Node {
/** The contents of the node is public */
public E elt;
protected Node prev, next;
Node() {
this(null);
}
Node(E elt) {
this.elt = elt;
prev = next = null;
}
}
Node first, last;
DLList() {
first = last = null;
}
// inserts an element at the beginning of the list
public Node addFirst(E e) {
Node node = new Node(e);
if(first==null){
first = node;
last = node;
}else{
node.next = first;
first.prev = node;
first = node;
}
return node;
}
}
In the else-block of the addFirst-function the variable next is set to the reference first and two lines later the reference first is set to the Node-object node. Surspringly (to me) this works. Shouldn't this mean that node.next is actually set to node as we basically get node.next = first = node?
EDIT:
Answers:
You're changing references (pointers) - which is why it does [work]. The last line first = node; simply changes first from pointing to the previous node to point to the current node. – alfasin
I think I figured it out. In my code I am not changing the actual object, I am just changing what objects are being referenced. In plain english my code in the else-block can be read as:
1. Set node.next to reference the object that first is referencing.
2. Set first.prev to reference the object that node is referencing.
3. Lastly, reassign first to reference the object that node is referencing. – erikejan
There are some issues in your code as mentioned in the comments, but to answer your question, no, it's not the same. In Java, you assign variables by value and not by reference. So, if you modify first after assigning it to node, it doesn't modify the value of node.
It's like,
a = 5;
b = a;
a = 4;
Here, value of b will be 5. It doesn't get changed to 4.
Compiler executes each statement sequentially. So it will not know that the value of a will be modified in the future or not.
When given an array of integers, I'm trying to change each element with the product of the integers before it.
For example, int[] array = {2,2,3,4}; is now: {2, 4, 12, 48};
I added each element to a LinkedList, and I'm trying to do this recursively.
This is what I have:
Node curr = list.getFirst();
product(curr);
public static void product(Node curr)
{
if(curr == null)
{
return;
}
else
{
int data = curr.getData() * curr.getNext().getData();
Node newNode = new Node(data);
curr.setNext(newNode);
// product(curr);
}
}
The first product works: {2,4}, but when I try to put in the recursion, I get a stackoverflow. Any suggestions??
Edit: So the reason that I'm either getting a stackoverflow or null pointer exception is because I'm updating the list, and then trying to get the next integer(but since there's only two elements in the list, there isn't a getNext()). I'm not sure how to fix this.
It looks like you were getting a bit tied up in the recursion. I modified your method to accept a Node along with the product from the previous iteration. At each step of the iteration I update the value in the already-existing List, so there is no need for using the new operator.
public static void product(Node curr, int value) {
if (curr == null) {
return;
}
else {
int data = value * curr.getData(); // compute current product
curr.setData(data); // update Node
product(curr.getNext(), data); // make recursive call
}
}
There are actually two issues with the code.
The recursion never ends, i.e. it is not actually moving to a smaller "subproblem" as the recursion is calling the same node again
and again.
After creating a new node and modifying the next we also need to connect the node "after" the next node otherwise the link will be
lost. Please check the below method which addresses both the issues.
Although I didn't do an excessive testing it is working for simple dataset.
Original List:
2->4->5->6->8->null
Multiplied List:
2->8->40->240->1920->null
public void product(Node curr) {
if (curr.getNext() == null) {
return;
} else {
int data = curr.getData() * curr.getNext().getData();
Node newNode = new Node();
newNode.setData(data);
Node nodeAfterNextNode = curr.getNext().getNext();
newNode.setNext(nodeAfterNextNode);
curr.setNext(newNode);
product(newNode);
}
}
It is because you call recursive method on the current node, so it is actually never move forward in the LinkedList. You can simply update the next node's data and call the recursive method on it. See the code below:
Node curr = list.getFirst();
product(curr);
public static void product(Node curr)
{
Node next = curr.getNext();
if(next == null)
{
return;
}
else
{
int data = curr.getData() * next.getData();
next.setData(data);
product(next);
}
}
I am new to java.
The question might not be that clear. Let me explain using code.
The code below is a function that prints out values in a linkedlist.
The first line of the function creates a reference to point to the same object that is pointed by the HeadNode reference. So any changes to currentNode will affect the object that HeadNode is pointing to.
Now, inside the while loop, I am making the change of the currentNode, but I observe the linkedList didn't change after I exit the function. Why?
static void PrintLinkedList(ListNode HeadNode)
{
ListNode currentNode = HeadNode;
while(currentNode != null)
{
System.out.println(currentNode.getData());
currentNode = currentNode.getNext();
}
}
UPDATE:
the reason of me bring this quesiton up is that when I implemented a function to reverse a linkedlist, my tempNode become null after the following code executed:
static ListNode ReverseLinkedList(ListNode headNode)
{
ListNode headNodeTemp = headNode;
headNodeTemp.setNext(null);
ListNode tempNode = headNode.getNext(); //temp becomes null because headNode is changed. Why??
ListNode currentNode = headNodeTemp;
while(tempNode != null)
{...
So any changes to currentNode will affect the object that HeadNode is pointing to.
No, currentNode is a reference to the object HeadNode also refers to. You never change that object, all you change is what currentNode refers to.
currentNode = currentNode.getNext();
makes currentNode point to the next object, it doesn't change any object.
To change the object, you need to call a method or set a property,
currentNode.mutate();
currentNode.property = 17;
would change the pointed-to object, and these changes would be visible when the object is queried through HeadNode.
currentNode = currentNode.getNext();
just makes currentNode point to another object.
In
static ListNode ReverseLinkedList(ListNode headNode)
{
ListNode headNodeTemp = headNode;
headNodeTemp.setNext(null);
here, we call a method that changes the pointed-to object. Going by the name, it sets the next field to null. And the object headNodeTemp points to is the object headNode points to.
ListNode tempNode = headNode.getNext(); //temp becomes null because headNode is changed. Why??
ListNode currentNode = headNodeTemp;
while(tempNode != null)
{...
You should move the
ListNode tempNode = headNode.getNext();
line above the setNext(null) line.
static ListNode ReverseLinkedList(ListNode headNode)
{
ListNode headNodeTemp = headNode;
ListNode tempNode = headNode.getNext();
headNodeTemp.setNext(null);
ListNode currentNode = headNodeTemp;
while(tempNode != null)
{...
You're confusing objects and references.
currentNode = x does not change the object that currentNode points to. Rather, it changes the currentNode variable to point to a different object.
That does not affect any other fields or variables that happen to point to the old object.
In agreement with the other posts. All arguments are pass-by-value in Java - Demonstration:
public static void main(String[] args) {
List a = new ArrayList();
a.add("Fred");
changeA(a);
// contents of a is "Fred" not "Barney" (change to 'a' not reflected back here)
// because all arguments are Pass-By-Value
addToA(a);
// contents of a is "Fred" AND "Barney"
int b = 10;
changeB(b);
// b = 10 (not 20)
}
static void changeA(List a) {
a = new ArrayList(); // a is now a different object
a.add("Barney");
}
static void addToA(List a) {
a.add("Barney");
}
static void changeB(int b) {
b = 20;
}