Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Java (http://www.programmingforums.org/forum17.html)
-   -   why does it change both instances (http://www.programmingforums.org/showthread.php?t=13986)

cwl157 Sep 18th, 2007 3:58 PM

why does it change both instances
 
Alright, so i have a class that contains a 2d array. I make 2 instances of it with the same 2d array arrangement in both of them. If i change one of them the other changes as well for some reason. I can not figure out why both change. Shouldn't only the one instance change?
Here is the node class
:

import java.io.*;
public class GridNode
{
  private int[][] state;
  private GridNode firstChild;
  private GridNode nextSibling;
 
  // set state to newState and firstChild to newFirstChild and nextSibling to newNextSibling
  public GridNode(int[][] newState, GridNode newFirstChild, GridNode newNextSibling)
  {
      state = newState;
      firstChild = newFirstChild;
      nextSibling = newNextSibling;
  } // end GridNode constructor
 
  // get current state
  public int[][] getState()
  {
      return state;
  } // end getState
 
  public void setState(int[][] newState)
  {
      state = newState;
  } // end setState
 
  // set firstChild
  public void setFirstChild(GridNode newFirstChild)
  {
      firstChild = newFirstChild;
  } // end setFirstChild
 
  // set nextSibling
  public void setNextSibling(GridNode newNextSibling)
  {
      nextSibling = newNextSibling;
  } // end setNextSibling
 
  // get firstChild
  public GridNode getFirstChild()
  {
      return firstChild;
  } // end getFirstChild
 
  // get nextSibling
  public GridNode getNextSibling()
  {
      return nextSibling;
  } // end getNextSibling
 
  // print the current state
  public void printNode()
  {
      for(int row = 0; row < 3; row++)
      {
        for(int col = 0; col < 3; col++)
          System.out.print(state[row][col] + "  ");
       
        System.out.println();
      } // end for
    } // end printNode
 
  // check to see if current state is equal to the goal state
  public boolean isEqual(int[][] state, int[][] goalState)
  {
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == goalState[row][col])
              continue;
           
            else
            return false;
        } // end for
      }  // end for
      return true;
  } // end isEqual
 
  // calculates h1(n)
  public int findHone(int[][] state, int[][] goalState)
  {
      int hOne = 0;
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] != goalState[row][col] && state[row][col] != 0)
              hOne++;
           
            else
            continue;
        } // end for
      }  // end for
      return hOne;
  } // end findHone
 
  // find f(n) = g(n) + h(n)
  public int findF(int g, int h)
  {
      return g + h;
  } // end findF
 
  // check to see if it can move left
  public boolean canMoveLeft(int[][] state)
  {
      int tempPos = -1;
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == 0)
                tempPos = col;
        } // end for
      } // end for
     
      if (tempPos == 0)
        return false;
      else
      return true;
  } // end canMoveLeft
 
  // check to see if it can move up
  public boolean canMoveUp(int[][] state)
  {
      int tempPos = -1;
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == 0)
                tempPos = row;
        } // end for
      } // end for
     
      if (tempPos == 0)
        return false;
      else
      return true;
  } // end canMoveUp
 
  // check to see if it can move right
  public boolean canMoveRight(int[][] state)
  {
      int tempPos = -1;
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == 0)
                tempPos = col;
        } // end for
      } // end for
     
      if (tempPos == 2)
        return false;
      else
      return true;
  } // end canMoveRight
 
  // check to see if it can move down
  public boolean canMoveDown(int[][] state)
  {
      int tempPos = -1;
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == 0)
                tempPos = row;
        } // end for
      } // end for
     
      if (tempPos == 2)
        return false;
      else
      return true;
  } // end canMoveDown
 
    // move it left, the old one
  public void moveLeft(int[][] state)
  {
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == 0)
            {
                  state[row][col] = state[row][col-1];
                  state[row][col-1] = 0;
                  //printNode();
                  return;
            } // end if
        } // end for
      } // end for
  } // end moveLeft
 
 
  // move it Up
  public void moveUp(int[][] state)
  {
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == 0)
            {
                  state[row][col] = state[row-1][col];
                  state[row-1][col] = 0;
                // printNode(state);
                  return;
            } // end if
        } // end for
      } // end for
  } // end moveUp
 
    // move it right
  public void moveRight(int[][] state)
  {
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == 0)
            {
                  state[row][col] = state[row][col+1];
                  state[row][col+1] = 0;
                  // printNode(state);
                  return;
            } // end if
        } // end for
      } // end for
  } // end moveRight
 
    // move it down
  public void moveDown(int[][] state)
  {
      //int tempPos = -1;
      for (int row = 0; row < 3; row++)
      {
        for (int col = 0; col < 3; col++)
        {
            if (state[row][col] == 0)
            {
                state[row][col] = state[row+1][col];
                state[row+1][col] = 0;
                // printNode(state);
                return;
            } // end if
        } // end for
      } // end for
  } // end moveDown
 
} // end GridNode


main where the 2 instances and everything is created
:

public class NewMain
{
  //insert new value, check to see if swap is needed
/* static void realInsert(GridNode parent, GridNode newNode)
  {
      if (parent.getFirstChild() == null)
        parent.setFirstChild(newNode);
      else
        realInsert(parent.getFirstChild().getNextSibling(), newNode);
      if(parent.canMoveLeft(parent.getState()))
      {
        parent.moveLeft(parent.getState());
        System.out.println("parent after move left:");
        parent.printNode();
        newNode.setState(parent.getState());
        System.out.println();
        System.out.println("newNode after setState is called");
        newNode.printNode();
        parent.moveRight(parent.getState());
        System.out.println();
        System.out.println("parent after move back happens");
        parent.printNode();
        System.out.println("one final print out of newNode");
        newNode.printNode();
      } // end if
  */   
 // } // end realInsert
  public static void main(String[] args)
  {
      int[][] initialState = {{7, 6, 4},
                            {8, 5, 2},
                            {1, 0, 3}
                            };
      GridNode parent = new GridNode(initialState, null, null);
      GridNode child = new GridNode(parent.getState(), null, null);
 
      parent.setFirstChild(child);
     
      child.moveLeft(child.getState());
      System.out.println("this is the child");
      child.printNode();
     
      System.out.println();
      System.out.println("This is the parent");
      parent.printNode();
     
     
      System.out.println();
      System.out.println("this is the initial state");
      for (int i = 0; i < 3; i++)
      {
          for (int j = 0; j < 3; j++)
          {
              System.out.print(initialState[i][j] + "    ");
          }
          System.out.println();
      }
     
     
  } // end main
 
} // end NewMain


titaniumdecoy Sep 18th, 2007 6:43 PM

Object variables in Java are references (similar to pointers in C). If you need an independent copy of an object you need to make it; this is usually done by implementing the Cloneable interface and overriding the clone method to create and return a new copy of the object. You might also want to investigate the difference between a "shallow" copy and a "deep" copy.

cwl157 Sep 18th, 2007 6:52 PM

i thought that when a new instance of a class is created that a new copy of each of the methods and variables of that class went along with it. So you see the int[][] state. Lets say when i create a new GridNode, a new int[][] state is not created along with it? Then how do i do this? I do I make it so that when i say
:

GridNode n = new GridNode(initialState, null, null);

How do i make it so that it will create a new initialState, another copy of int[][] for the new node. I am going to be doing minor manipulations to each new instance so my goal was to just create a copy of the first instances int[][] into the other ones and then do the manipulations on the other ones. But if i can't just do that with out it taking the old one with it, then what do i do? I have never heard of clonable or anything. i've been stuck on this problem for over 2 days and just want to know what im doing wrong and make it work properly. Thanks.

titaniumdecoy Sep 18th, 2007 7:23 PM

Now that I look more closely at your problem, I see that it is not object copying you are having trouble with but rather Java's strange implementation of arrays. Strangely enough, an array in Java is a actually a reference to an "array object" which holds references to its elements. So when you create a new GridNode as you do in the line of code you posted above, the variable n points to a distinct GridNode object. However, when you pass the initialState 2D array as an argument to the constructor, the state variable of the GridNode is a reference the original 2D array. The simplest solution is to simply call the array's clone method to create a new array. (Alternatively you could simply loop over each element and copy it into a new array; see this article for other methods.) The following example might help:

:

int[] p = { 1, 2, 3, 4 };
int[] q = p;

q[0] = 5;

System.out.println("p" + Arrays.toString(p) + ", q" + Arrays.toString(q));
// Output: p[5, 2, 3, 4], q[5, 2, 3, 4]

q = (int[])p.clone();

q[0] = 1;

System.out.println("p" + Arrays.toString(p) + ", q" + Arrays.toString(q));
// Output: p[5, 2, 3, 4], q[1, 2, 3, 4]


cwl157 Sep 18th, 2007 7:47 PM

so i think i get what your saying but then how would i make that happen because i am going to be creating like 17 or so of these GridNodes, each with a different 2d array associated to it. Should i have a method in the GridNode class that does this and then call that whenever i create a new instance or something? I tried this just to see if i could get it to work and it didnt.
:

 
      GridNode parent = new GridNode(initialState, null, null);
      GridNode child = new GridNode(initialState, null, null);
      child.setState((int[][]) parent.getState().clone());


I also tried this and it didnt work either
:

GridNode parent = new GridNode(initialState, null, null);
      GridNode child = new GridNode((int[][]) parent.getState().clone(), null, null);


titaniumdecoy Sep 18th, 2007 8:52 PM

Okay, you have another (big) problem: When you name a parameter of a method the same as an instance variable of that class, the parameter variable "hides" the instance variable. So, in this snippet of code, for example:

:

public class GridNode
{
    private int[][] state;
   
    ...
   
    public void moveRight(int[][] state) {
        for (int row = 0; row < 3; row++) {
            for (int col = 0; col < 3; col++) {
                if (state[row][col] == 0) {
                    state[row][col] = state[row][col+1];
                    state[row][col+1] = 0;
                    return;
                }
            }
        }
    }
   
    ...
   
}

The state parameter, not the state instance variable, is being changed! As you can image, all sorts of problems will arise. (The original array, presumably declared in NewMain, will be altered, for the reasons described in my last post.) The solution: Either don't name any of your parameters the same as any of your instance variables or use the this keyword to qualify variables:

:

this.state[row][col] = state[row][col+1];
All that said, however, this is not the cause of your problem--and perhaps you intended that behavior. If so, the method should be declared static (and you might want to consider changing the parameter name anyway for obvious reasons).

titaniumdecoy Sep 18th, 2007 9:05 PM

The cause of your problem is the fact that the clone method of a 2D array creates a shallow copy. (Stupid? Yes.) Think of a 2D array as an object with X references to Y 1D arrays (each of which contain additional references--the actual objects stored in the array). The clone method of a 2D array creates a new 2D array--but copies the references to the 1D arrays rather than cloning them! You can avoid this by writing your own method to copy a 2D array:

:

public static int[][] copy(int[][] a) {
    int[][] b = new int[a[0].length][a.length];
    for (int i = 0; i < a.length; i++)
        for (int j = 0; j < a[0].length; j++)
            b[i][j] = a[i][j];
    return b;
}

Hopefully that will solve your problem. :)

DaWei Sep 18th, 2007 9:11 PM

Titanium is giving you the scoop, but you don't seem to get it. Let me make an analogy. Suppose you post a memo on the bulletin board, very top left corner. Someone asks you what the memo says. You refer them to the upper left corner. Someone else asks you what the memo says. You refer them to the upper left corner.

If someone else comes along and scratches out part of the memo and replaces it, all referrals to the upper left corner memo look the same. The ARE the same.

That's a reference. If you give someone a note that says, "Upper left corner", and someone else a note that says, "Upper left corner", that's two different notes, but the reference is the same, The second note is a shallow copy of the first.

On the other hand, if you copy the memo and hand that out, instead of a reference to the bulletin board, that's a deep copy. If only Joe's is changed, Fred doesn't see that.

cwl157 Sep 18th, 2007 9:42 PM

alright so now if i wanted to make a tree with one child and then the child having up to 3 siblings all consisting of GridNodes but each int[][] state of the GridNodes would be different, how would i do that. I get the firstChild points to the child and then the nextSibling would point to the sibling and then i set those to null when i first create the instance and then when needed set them but i need to keep track of what level of the tree im in and all that too. I'm just really lost when it comes to trees so i took a class that deals with them hoping to learn only to find out today that the teacher doesn't help anyone so im actually kinda lost right now.

Alright so this is what i got on how to get it to work like i want it to
:

int[][] initialState = {{7, 6, 4},
                            {8, 5, 2},
                            {1, 0, 3}
                            };
     
      //int[][] childState = parent.copy(parent.getState());
      GridNode parent = new GridNode(initialState, null, null);
      int[][] childState = parent.copy(parent.getState());
      GridNode child = new GridNode(childState, null, null);


but now i ask if im gonna be creating a bunch of children, is there a way for me to do it without having to create a new corresponding int[][] along with it? Because then to refer to it instead of saying child.getState() to get the state couldnt i just not even use the class and just create a new int [][] within this class and just refer to that for everything?

cwl157 Sep 18th, 2007 10:20 PM

i think i got it. This is what the if statement looks like for making the left child of the tree

:

if (parent.canMoveLeft(parent.getState()))
    {
        child.setState(parent.copy(parent.getState()));
        child.moveLeft(child.getState());
        parent.setFirstChild(child);
        System.out.println(parent.getFirstChild());
        child.printNode();
        System.out.println();
        parent.printNode();
    } // end if



All times are GMT -5. The time now is 4:13 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC