This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 2 years ago.
The code below shows a data structure in leetcode. We can see the node1 store a list with node2 and node3, node2 will store a list with node1 and node4. In this case I think node1 and node2 will store the object of each others, which will cause an infinite recursion. How does java store the data structure like this, doesn't it cause a memory exceed?
class Node {
public int val;
public List<Node> neighbors;
public Node() {
val = 0;
neighbors = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
neighbors = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _neighbors) {
val = _val;
neighbors = _neighbors;
}
}
public static void main(String[] args) {
Node node1 = new Node(1, new ArrayList<>());
Node node2 = new Node(2, new ArrayList<>());
Node node3 = new Node(3, new ArrayList<>());
Node node4 = new Node(4, new ArrayList<>());
node1.neighbors.add(node2);
node1.neighbors.add(node4);
node2.neighbors.add(node1);
node2.neighbors.add(node3);
node3.neighbors.add(node2);
node3.neighbors.add(node4);
node4.neighbors.add(node1);
node4.neighbors.add(node3);
Solution solution = new Solution();
solution.cloneGraph(node1);
}
You would be correct about this code causing memory to be exceeded if each node's list of neighbours contained copies of those neighbours. But that's not how Java works. Instead the list contains references to the neighbour nodes.
As an analogy, if each time you wrote down someone's address you need a complete copy of their house then you'd run out of space quickly. But you don't - you just need a reference to their house which can itself contain a reference to yours.
Note that's it's pretty easy to write code that causes a stack overflow with objects that contain references to themselves. For example, if your class had a method:
class Node {
public int sumVals() {
return val + neighbours.stream().mapToInt(Node::sumVals).sum();
}
}
calling node1.sumVals() will cause infinite recursion.
Related
This question already has answers here:
How to clone ArrayList and also clone its contents?
(21 answers)
Closed 3 years ago.
I have a Node object and want to make some clone objects from this orginal one. Below is my code:
public class Main {
public static void main(String[] args) {
Node node = new Node();
node.setName("node1");
node.setValue("1");
node.setChildNodes(new ArrayList<>());
Node node2 = cloneNode(node);
Node childNode2 = new Node();
childNode2.setName("childNode2");
childNode2.setValue("1");
List<Node> childNodes2 = node2.getChildNodes();
childNodes2.add(childNode2);
node2.setChildNodes(childNodes2);
System.out.println(node.getChildNodes());
}
private static Node cloneNode(Node node) {
Node newNode = new Node();
newNode.setName(node.getName());
newNode.setValue(node.getValue());
newNode.setChildNodes(node.getChildNodes());
return newNode;
}
}
class Node {
private String name;
private String value;
private List<Node> childNodes;
//getter and setter
}
I created a first Node and create a second Node which is cloned from the first one. Then I would like to modify the childNodes list in the second Node object (node2) and then I realize that the childNodes list in the first Node(node) is also modified.
How can I avoid the list in the first Node being modified?
Thank you so much!
What you have done is a kind of shallow clone. You might want to clone deeper instead of
newNode.setChildNodes(node.getChildNodes())
You should clone the child nodes as well. Proper way is this
Alternatively , you can just serialize and deserialize the object is the easy way of cloning deep objects
Eg: Just transform the Node1 to JSON string and convert the string back to Node2 (Tip: use the gson library)
so I have a list of basic nodes, for example nodes A B C.
each component can see what it is attached to for example:
a->b
b->c
c->a
I want a way that I can get a list of all the nodes in the graph. However, I'm running into trouble as my current system can't detect if it has already reached a point. EG in the above example it will go a->b->c->a->b etc. How can I detect this or how can I solve this problem.
My current "solution" getList() in the Node class:
ArrayList<Node> tempList = new ArrayList<Node>();
tempList.add(this);
for(int i = 0 ; i < nodesAttachedTo.size();i++){
tempList.addAll(nodesAttachedTo.get(i).getList());
}
return tempList;
You can use a HashSet. It will not allow one element to be added twice.
Here's an example code that first creates the graph similar to your example, then starts at some point in the graph and goes through it.
import java.util.HashSet;
public class Node
{
private HashSet<Node> nextNodes = new HashSet<Node>();
public Node()
{
}
public void addNextNode(Node node)
{
nextNodes.add(node);
}
public static void main(String[] args)
{
// this builds the graph of connected nodes
Node a = new Node();
Node b = new Node();
Node c = new Node();
a.addNextNode(b);
b.addNextNode(c);
c.addNextNode(a);
//this is the set that will lsit all nodes:
HashSet<Node> allNodes = new HashSet<Node>();
// this goes through the graph
a.listAllNodes(allNodes);
System.out.println(allNodes);
}
private void listAllNodes (HashSet<Node> listOfNodes)
{
// try to put all next nodes of the node into the list:
for(Node n : nextNodes)
{
if (listOfNodes.add(n)) // the set returns true if it did in fact add it.
n.listAllNodes(listOfNodes); // recursion
}
}
}
This goes from one node to all the nodes this node knows. (say that really fast three times)
Until it hits a dead end (= a node it already visited)
I chose to use a HashSet in the Node itself to store all the nodes it knows.
This could also be an ArrayList or whatever. But as I don't think there should be a connection twice, a HashSet seems to be a good choice in this situation, too.
I'm not familiar with your notation, but you could use two pointers to solve your issue. Start with two pointers that point to the same node. Increment one pointer until it returns to the start. Some pseudocode is below.
ArrayList<Node> tempList = new ArrayList<Node>();
Node head = nodesAttachedTo.get(0); //get the head of the list
tempList.add(head);
Node runner = head;
runner = runner.next;
while (!runner.equals(head)) {
tempList.add(runner);
runner = runner.next;
}
A hashmap is probably the way to go here. It allows constant time access (some overhead required, but I'm assuming you want a solution that scales well) to any element in the map.
HashMap<String, String> specificSolution = new HashMap<String, String>();
specificSolution.put("a", "b");
specificSolution.put("b", "c");
specificSolution.put("c", "a");
// To get all nodes in the graph
Set<String> nodes = specificSolution.keySet();
I implemented with String here because you don't provide a definition for the Node Class in your question, but it can be easily swapped out.
There are many different ways to represent a graph and each has their own limitations/advantages. Maybe another might be more appropriate but we would need more information about the problem.
i'm doing a project and its to do with binary trees and botanical keys, now what i need to do load a bot key into the programme, then work be able to work my way through it going to each node in order to determine the type of tree it is, and it will display the tree itis when i reach a leaf node. Now im having trouble with the moving through it part, i cant figure out how to get it to work, ive tried many different things including a pre oder traversal, and i was just wondering if anyone could give me a hand in getting it off the ground so to speak. the code for the part of the programme i need to edit is below, the "chooseOption" method is the method used to traverse the tree.
thanks in advance.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Stack;
/**
*
* #author Alex Drinkwater 10077473
*/
public class BotKey implements IBotKey {
private Node root;
private Node currentNode;
// This stack will come in handy when you implement the "undo" facility
private Stack<Node> choiceStack = new Stack<Node>();
#Override
public boolean isLoaded() {
return root != null;
}
#Override
public int getNbrChildren() {
if (currentNode == null) {
return 0;
} else {
return currentNode.getNbrChildren();
}
}
#Override
public String getLeadStatement(int i) {
return currentNode.getChildStatement(i);
}
#Override
public void chooseOption(int i) {
//You need to implement this method
if (root != null) {
root.getChildNode(i);
root.getChildStatement(i);
}
}
#Override
public void undoChoice() {
//You need to implement this method
}
#Override
public String getResults() {
//Dummy implementation. You need to supply a proper one
if (currentNode.isLeaf() == true) {
currentNode.getName();
}
else{
String s =currentNode + "";
if(currentNode!= null){
}
}
return "s";
}
#Override
public void loadExample1() {
Node southernLive = new Node("Southern live Oak: Quercus virginiana");
Node dwarfLive = new Node("Dwarf live oak: Quercus minima");
Node willow = new Node("Willow oak");
Node shingle = new Node("Shingle oak");
Node blackJack = new Node("Blackjack Oak");
Node northernRed = new Node("Northern red oak");
Node white = new Node("White oak Quercus alba");
Node swampChestnut = new Node("Swamp chestnut oak Quercus prinus");
Node node7 = new Node();
node7.addChild(white, "Leaves with 5-9 deep lobes");
node7.addChild(swampChestnut, "Leaves with 21-27 shallow lobes");
Node node6 = new Node();
node6.addChild(blackJack, "Leaves mostly with 3 lobes");
node6.addChild(northernRed, "Leaves mostly with 7-9 lobes");
Node node5 = new Node();
node5.addChild(node7,
"Lobes or teeth rounded or blunt-pointed, no bristles");
node5.addChild(node6, "Lobes or teeth bristle-tipped");
Node node4 = new Node();
node4.addChild(willow, "Leaf narrow, about 4-6 times as long as broad");
node4.addChild(shingle, "Leaf broad, about 2-3 times as long as broad");
Node node3 = new Node();
node3.addChild(dwarfLive, "Mature plant a small shrub");
node3.addChild(southernLive, "Mature plant a large tree");
Node node2 = new Node();
node2.addChild(node4, "Leaves not evergreen");
node2.addChild(node3, "Leaves evergreen");
Node node1 = new Node();
node1.addChild(node5, "Leaves usually with teeth or lobes");
node1.addChild(node2, "Leaves usually without teeth or lobes");
root = node1;
currentNode = root;
}
#Override
public void loadExample2() {
Node southernLive = new Node("Southern live Oak: Quercus virginiana");
Node dwarfLive = new Node("Dwarf live oak: Quercus minima");
Node willow = new Node("Willow oak");
Node shingle = new Node("Shingle oak");
Node blackJack = new Node("Blackjack Oak");
Node northernRed = new Node("Northern red oak");
Node white = new Node("White oak Quercus alba");
Node swampChestnut = new Node("Swamp chestnut oak Quercus prinus");
Node node7 = new Node();
node7.addChild(white, "Leaves with 5-9 deep lobes");
node7.addChild(swampChestnut, "Leaves with 21-27 shallow lobes");
Node node6 = new Node();
node6.addChild(blackJack, "Leaves mostly with 3 lobes");
node6.addChild(northernRed, "Leaves mostly with 7-9 lobes");
Node node5 = new Node();
node5.addChild(node7,
"Lobes or teeth rounded or blunt-pointed, no bristles");
node5.addChild(node6, "Lobes or teeth bristle-tipped");
Node node4 = new Node();
node4.addChild(willow, "Leaf narrow, about 4-6 times as long as broad");
node4.addChild(shingle, "Leaf broad, about 2-3 times as long as broad");
Node node3 = new Node();
node3.addChild(dwarfLive, "Mature plant a small shrub");
node3.addChild(southernLive, "Mature plant a large tree");
Node node2 = new Node();
node2.addChild(node4, "Leaves not evergreen");
node2.addChild(node3, "Leaves evergreen");
Node node1 = new Node();
node1.addChild(node5, "Leaves usually with teeth or lobes");
node1.addChild(node2, "Leaves usually without teeth or lobes");
root = node1;
currentNode = root;
//You need to implement this method
}
#Override
public void readFromFile(FileInputStream fs) {
//You need to implement this method
// You may find that a hashmap like this is useful
HashMap<Integer, Node> map = new HashMap<Integer, Node>();
}
#Override
public boolean canUndo() {
//Dummy implementation. You need to provide a proper one
if (choiceStack != null) {
return true;
} else {
return false;
}
}
}
You have a botanical "database" (in tree structure), that describes each plant, for example: "Willow oak"? Then you want to understand what was given for the search? For example we have a description of some plant: "Leaves with 5-9 deep lobes" and we want to get all plants that this plant could be? In that case we need to find a Node wich contains text "Leaves with 5-9 deep lobes". Let's call this node startingNode.
Then we have to move from startingNode to it's parent. Let's call startingNode's parent a parentNode. Then we have to get a parent of parentNode and so on. Repeat until you reach a node which has a name for the plant.
This question should be rather easy for any Java developer. I swear I looked it up after spending ~2 hours on it, but I can't really understand what's wrong with this code.
Basically, I am implementing Karger's minimum cuts algorithm. It requires me to keep merging nodes in a graph and then compute the number of crossing edges at the end (an int value). This algorithm must be repeated n times, always from the starting graph. My problem is that I am unable to create a deep copy of my Graph object, and I can't find the mistake.
I have cropped the code to just show the problem and no more, but I am still unable to figure out what's wrong. Here the code is.
Class Node:
public class Node {
public Integer Data;
public Node() {
Data = 0;
}
public Node(Node rhs) {
Data = rhs.Data.intValue();
}
public Node(Integer rhs) {
Data = rhs.intValue();
}
public void setNode(Integer rhs) {
Data = rhs;
}
Class Graph:
public class Graph {
public ArrayList<ArrayList<Node>> AdjList;
public ArrayList<Node> NodeSet; // This contains all the nodes
public Graph() {
AdjList = new ArrayList<ArrayList<Node>>();
NodeSet = new ArrayList<Node>();
}
public Graph(Graph G) {
AdjList = new ArrayList<ArrayList<Node>>();
for (ArrayList<Node> L : G.AdjList) {
ArrayList<Node> Lcopy = new ArrayList<Node>();
for (Node N : L) {
Node copy = new Node(N);
Lcopy.add(copy);
}
AdjList.add(L);
}
}
public void addNewAdjList(ArrayList<Node> NodeAdjList) {
// Input is the adjacency list of a new node
// The first element in the NodeAdjList is the node itself, the rest is the adj nodes
AdjList.add(NodeAdjList);
}
public static void printAdjList(ArrayList<Node> Adjlist) {
Node start = Adjlist.get(0);
System.out.print(start.Data + " : ");
for (int j=1; j < Adjlist.size(); ++j) {
System.out.print(Adjlist.get(j).Data + ", ");
}
System.out.print("\n");
}
Main:
public class Main {
/**
* #param args
*/
public static void main(String[] args) {
Node Five = new Node(5);
Node Seven = new Node(7);
Node One = new Node(1);
Graph G = new Graph();
ArrayList<Node> L = new ArrayList<Node>();
L.add(Five);
L.add(Seven);
L.add(One);
G.addNewAdjList(L);
Graph R = new Graph(G);
R.AdjList.get(0).get(1).setNode(19); // Gets node #1 in the first adj list, i.e. 7
Graph.printAdjList(G.AdjList.get(0));
Graph.printAdjList(R.AdjList.get(0));
}
}
Output:
5 : 19, 1,
5 : 19, 1,
This kind of puzzles me to be honest. I understand that Java is pass by value only, but objects are always represented by their reference. As far as I understand, my copy constructor for G should always make a deep copy: I am moving through every adjacency list and then I am making a deep copy of the Node. I don't understand why invoking .setNode() on the copied object modifies also the original object (that has a different reference).
Previous answers like 1 seem to go the same direction I am going, what am I missing here? :S
Your error is here:
ArrayList<Node> Lcopy = new ArrayList<Node>();
for (Node N : L) {
Node copy = new Node(N);
Lcopy.add(copy);
}
AdjList.add(L);
You created a copy of L (called Lcopy) but then you added the original L to your cloned graph. To fix it the last line should be this:
AdjList.add(Lcopy);
Note: If you have used a sensible name for your variable instead of L this error would probably never have happened!
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I'm getting a NullPointerException when I try to run this code. I've assigned Nodes 2,3,and 4 as children nodes to Node1. I tried to create a method that will go through all the children nodes in Node1 and return the list. I'm not sure what I'm doing wrong.
public class TestingArrays2 {
List<Node> myList1;
List<Node> myList2;
List<Node> myList3;
List<Node> myList4;
private Node Node1;
private Node Node2;
private Node Node3;
private Node Node4;
public TestingArrays2() {
myList1 = new ArrayList<Node>();
myList2 = new ArrayList<Node>();
myList3 = new ArrayList<Node>();
myList4 = new ArrayList<Node>();
myList1.add(Node2);
myList1.add(Node3);
myList1.add(Node4);
Node1 = new Node("One", myList1);
Node2 = new Node("Two", myList2);
Node3 = new Node("Three", myList3);
Node4 = new Node("Four", myList4);
List<Node> allNodesArray = nodeArray(Node1);
for (int i = 0; i < allNodesArray.size(); i++){
System.out.println(allNodesArray.get(i).label);
}
}
public static void main(String arg[]) {
TestingArrays2 testArray = new TestingArrays2();
}
public List<Node> nodeArray(Node n){
List<Node> tempList = new ArrayList<Node>();
for (int i = 0; i < n.children.size(); i++){
tempList.add(n.children.get(i));
}
return tempList;
}
}
You're not creating your Nodes. See these lines...
private Node Node1;
private Node Node2;
private Node Node3;
private Node Node4;
These just declare a variable as being able to contain an object of type Node. However, they initially start with a null value - ie they're empty.
You're then calling these lines...
myList1.add(Node2);
myList1.add(Node3);
myList1.add(Node4);
Which would insert null values into your List, because you're trying to add an object that hasn't been created yet.
So, you need to change your code so that these lines...
Node1 = new Node("One", myList1);
Node2 = new Node("Two", myList2);
Node3 = new Node("Three", myList3);
Node4 = new Node("Four", myList4);
appear before you try to myList1.add() them to the list. This will create the Node objects first, which can then be added to your List.
As #BalusC mentioned in the comments, it is failing on your for loop later in your code, because it is trying to call .label on a null object. Correcting the order as suggested above will correct this, as all the objects in your List will now be Nodes.
This:
myList1.add(Node2);
myList1.add(Node3);
myList1.add(Node4);
Node1 = new Node("One", myList1);
Node2 = new Node("Two", myList2);
Node3 = new Node("Three", myList3);
Node4 = new Node("Four", myList4);
You are trying to add the nodes to the list before they have been created.
A good answer to your question is already given.
Looking at your code I have several suggested modifications.
You are doing all the work in (the constructor of) your test class. It is nicer design to delegate this to the Node class where possible. Also try not to do 'work' in the constructor, just initialization.
Also check out the code conventions I applied like using nouns for class names and starting variable names with a lower case letter.
public class ArrayTest2 {
public static void main(String arg[]) {
Node node1 = new Node("One");
node1.add(new Node("Two"));
node1.add(new Node("Three"));
node1.add(new Node("Four"));
// this calls the toString method of node1
System.out.println(node1);
}
}
public class Node {
private final String name;
private final List<Node> children;
public Node(String name) {
this.name = name;
this.children = new ArrayList<Node>();
}
public String getName() {
return name;
}
public void add(Node children) {
children.add(child);
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
if(children.size() > 0) {
sb.append("(");
String separator = "";
for (Node child : children){
sb.append(separator).append(String.valueOf(child));
separator = ",";
}
sb.append(")");
}
return sb.toString();
}
}
Notice that the children field is private and there is no getter for it. It is considered bad practice to offer direct access to internal data structures like 'children' so I provided an 'add' method for adding nodes. In this way the class keeps control over what happens to its data, which is an important OO design principle.
The toString method builds a string representation of a Node. It appends the Node name and then, in case there are children, appends each child node's string representation in a comma-separated list surrounded by parentheses, so this should print something like:
One(Two,Three,Four)
A more complex structure for example:
Node node1 = new Node("One");
Node node2 = new Node("Two");
Node node3 = new Node("Three");
Node node4 = new Node("Four");
Node node5 = new Node("Five");
Node node6 = new Node("Six");
node1.add(node2);
node1.add(node3);
node2.add(node4);
node4.add(node5);
node4.add(node6);
Should give:
One(Two(Four(Five,Six)),Three)
Disclaimer: my code is hand-crafted, uncompiled and untested