reifi
reifi

Reputation: 49

Deep copying an array of objects

I'm still pretty new to Java and right now I'm trying to make a copy of Menu. I think I've done a little bit of it where I created a new Menu object with new MenuItems in it. MenuItems is another class with two string variables and a double variable, the itemName and itemDescription and the itemPrice. So I'm trying to copy the contents, the three variables of the original MenuItems into the MenuItems copy, but I don't know how. I got stuck on trying to set the clone copy's name to the original's name.

public class Menu 
{
    Menu()
    {

    }

    final int maxItems = 50;

    MenuItem[] food = new MenuItem[maxItems + 1];


    public Object clone()
    {
        Menu menuClone = new Menu();
        MenuItem[] foodClone = new MenuItem[maxItems + 1];

        for(int i = 1; i <= maxItems + 1; i++)
        {  
            foodClone[i] = new MenuItem();
            foodClone[i] = food[i].setItemName();
        }

    }

This is the MenuItem class:

public class MenuItem 
{
    private String name;
    private String descrip;
    private double price;


    MenuItem()
    {

    }


    public String getItemName()
    {
        return name;
    }

    public String getItemDescrip()
    {
        return descrip;
    }

    public double getPrice()
    {
        return price;
    }

    public void setItemName(String itemName)
    {
        name = itemName; 
    }

    public void setItemDescrip(String itemDescrip)
    {
        descrip = itemDescrip;
    }

    public void setPrice(double itemPrice) throws IllegalArgumentException
    {
        if(itemPrice >= 0.0)
            price = itemPrice;
        else
            throw new IllegalArgumentException("Enter only positive values");
    }

    public String toString(){
        return "Name: " + name + ", Desc: " + descrip;
    }
}

Upvotes: 1

Views: 2811

Answers (2)

Dominic Holt
Dominic Holt

Reputation: 162

Cloning only provides a shallow copy, despite some of the previous recommendations.

A common solution to the deep copy problem is to use Java Object Serialization (JOS). The idea is simple: Write the object to an array using JOS’s ObjectOutputStream and then use ObjectInputStream to reconstitute a copy of the object. The result will be a completely distinct object, with completely distinct referenced objects. JOS takes care of all of the details: superclass fields, following object graphs, and handling repeated references to the same object within the graph. Figure 3 shows a first draft of a utility class that uses JOS for making deep copies.

import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;

/**
 * Utility for making deep copies (vs. clone()'s shallow copies) of 
 * objects. Objects are first serialized and then deserialized. Error
 * checking is fairly minimal in this implementation. If an object is
 * encountered that cannot be serialized (or that references an object
 * that cannot be serialized) an error is printed to System.err and
 * null is returned. Depending on your specific application, it might
 * make more sense to have copy(...) re-throw the exception.
 *
 * A later version of this class includes some minor optimizations.
 */
public class UnoptimizedDeepCopy {

/**
 * Returns a copy of the object, or null if the object cannot
 * be serialized.
 */
public static Object copy(Object orig) {
    Object obj = null;
    try {
        // Write the object out to a byte array
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        out.writeObject(orig);
        out.flush();
        out.close();

        // Make an input stream from the byte array and read
        // a copy of the object back in.
        ObjectInputStream in = new ObjectInputStream(
            new ByteArrayInputStream(bos.toByteArray()));
        obj = in.readObject();
    }
    catch(IOException e) {
        e.printStackTrace();
    }
    catch(ClassNotFoundException cnfe) {
        cnfe.printStackTrace();
    }
    return obj;
}

}

Unfortunately, this approach has some problems:

  1. It will only work when the object being copied, as well as all of the other objects references directly or indirectly by the object, are serializable. (In other words, they must implement java.io.Serializable.) Fortunately it is often sufficient to simply declare that a given class implements java.io.Serializable and let Java’s default serialization mechanisms do their thing.

  2. Java Object Serialization is slow, and using it to make a deep copy requires both serializing and deserializing. There are ways to speed it up (e.g., by pre-computing serial version ids and defining custom readObject() and writeObject() methods), but this will usually be the primary bottleneck.

  3. The byte array stream implementations included in the java.io package are designed to be general enough to perform reasonable well for data of different sizes and to be safe to use in a multi-threaded environment. These characteristics, however, slow down ByteArrayOutputStream and (to a lesser extent) ByteArrayInputStream.

Source: http://javatechniques.com/blog/faster-deep-copies-of-java-objects/

Upvotes: 0

NESPowerGlove
NESPowerGlove

Reputation: 5496

You are almost there, where you have:

foodClone[i] = food[i].setItemName();

You probably want (in addition to the other variables of MenuItem)

foodClone[i].setItemName(food[i].getItemName())`

However, it's best to use the clone method or a copy constructor (well, copy constructor arguably might be best).

I do prefer using a copy constructor, such an example would be:

MenuItem(MenuItem menuItemToClone)
{
     this.name = menuItemToClone.name;
     this.descrip = menuItemToClone.descrip;
     this.price = menuItemToClone.price;
}

Then you would just do:

foodClone[i] = new MenuItem(food[i]);

Upvotes: 1

Related Questions