Beruflich Dokumente
Kultur Dokumente
T E C H
T I P S
TIPS, TECHNIQUES, AND SAMPLE CODE
}
public class DeepDemo {
// shallow copy
static ArrayList copy1(ArrayList list) {
return (ArrayList)list.clone();
}
// hand-coded deep copy
static ArrayList copy2(ArrayList list) {
ArrayList newlist = new ArrayList();
A aobj = (A)list.get(0);
newlist.add(new A(aobj.getX()));
return newlist;
}
// deep copy via serialization
static ArrayList copy3(ArrayList list) throws Exception {
// serialize ArrayList into byte array
ByteArrayOutputStream baos =
new ByteArrayOutputStream(100);
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(list);
byte buf[] = baos.toByteArray();
oos.close();
// deserialize byte array into ArrayList
ByteArrayInputStream bais =
new ByteArrayInputStream(buf);
ObjectInputStream ois = new ObjectInputStream(bais);
ArrayList newlist = (ArrayList)ois.readObject();
ois.close();
return newlist;
}
static final int NUMITERS = 50000;
public static void main(String args[]) throws Exception {
ArrayList listold, listnew;
long currtime, elapsedtime2, elapsedtime3;
// shallow copy
listold = new ArrayList();
listold.add(new A(37));
listnew = copy1(listold);
((A)listold.get(0)).setX(47);
System.out.print("copy1 old/new = ");
System.out.print(((A)listold.get(0)).getX() + " ");
System.out.println(((A)listnew.get(0)).getX());
// deep copy via hand-coded method
listold = new ArrayList();
listold.add(new A(37));
currtime = System.currentTimeMillis();
for (int i = 1; i <= NUMITERS; i++) {
listnew = copy2(listold);
}
elapsedtime2 = System.currentTimeMillis() - currtime;
((A)listold.get(0)).setX(47);
System.out.print("copy2 old/new = ");
System.out.print(((A)listold.get(0)).getX() + " ");
System.out.println(((A)listnew.get(0)).getX());
// deep copy via serialization
listold = new ArrayList();
listold.add(new A(37));
currtime = System.currentTimeMillis();
for (int i = 1; i <= NUMITERS; i++) {
listnew = copy3(listold);
}
elapsedtime3 = System.currentTimeMillis() - currtime;
((A)listold.get(0)).setX(47);
System.out.print("copy3 old/new = ");
System.out.print(((A)listold.get(0)).getX() + " ");
System.out.println(((A)listnew.get(0)).getX());
System.out.println();
System.out.println("copy2 time = " + elapsedtime2);
System.out.println("copy3 time = " + elapsedtime3);
}
}
The DeepDemo program uses an ArrayList object containing a single
element of type A. Objects of type A are mutable through the setX
method.
The program defines three copy methods. The first method, copy1,
does a shallow copy using the ArrayList.clone method. This method
creates a new list and copies object references from the old list.
After the copy1 method is called, the program changes the value of
the single element in the old list and checks whether the value of
the element in the new list is also changed. Here, a change to the
new list indicates a shallow copy.
The second approach to copying uses a hand-coded method, copy2, to
make a deep copy. The method sets up a new ArrayList, gets the
value of the element from the old list, and adds a new A object
with this value to the new list.
The third approach, copy3, uses serialization. It uses writeObject
to convert the ArrayList into a byte stream stored in a
ByteArrayOutputStream object, and then reverses the process,
converting the stream of bytes back into an ArrayList. This
process forces the creation of new ArrayList and A objects.
When you run the program, the output should look something like
this:
copy1 old/new = 47 47
copy2 old/new = 47 37
copy3 old/new = 47 37
copy2 time = 47
copy3 time = 8500
Both hand-coded deep copying (copy2) and serialization (copy3)
keep the copies distinct. But there's an apparent issue here -performance. The serialization approach is much slower than the
hand-coded approach. However, this time discrepancy doesn't mean
that you should avoid serialization when making deep copies. The
DeepDemo results show that it took 8,500 milliseconds to make the
copies. If you examine DeepDemo, you'll see that it does the
copying 50,000 times. Doing 50,000 copies in 8,500 milliseconds
means that it didn't take very long to do a single copy. And time
might not be a critical factor in the part of your application
that makes these copies.
There's another big issue here besides performance. The manual or
hand-coded approach doesn't scale very well to complex data
structures. For example, if you need to make a deep copy of a
graph or tree structure, doing it yourself can get pretty
complicated. So comparing the serialization approach to the
manual approach, you can say that the serialization approach
costs a lot more, measured in time, than the manual approach, but
it does a lot more as well.
A final point about copying through serialization is that
all the objects that the serialization process encounters must be
serializable. In the DeepDemo example, A got this property by
implementing the java.io.Serializable marker interface. ArrayList
does likewise.
For more information about deep copying, see Section 3.9.3,
Shallow versus Deep Cloning, and Section 15.7, Object
Serialization, in "The Java(tm) Programming Language Third
Edition" by Arnold, Gosling, and Holmes
(http://java.sun.com/docs/books/javaprog/thirdedition/).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING STRICTFP
The keyword "strictfp" was a late addition to the Java
programming language. It is used to control certain aspects of
floating-point arithmetic. It can be a bit difficult to
describe and illustrate strictfp, so this tip will do it
in a stepwise way. The tip starts with a few examples of syntax,
then it presents an example that shows where strictfp might be
important.
You can use strictfp as a modifier of class, interface, and
method declarations, like this:
// legal uses of strictfp
strictfp interface A {}
public strictfp class FpDemo1 {
strictfp void f() {}
}
You cannot use strictfp on constructors or methods within
interfaces:
Can you do better than the standard approach? The answer is yes,
at least in some cases. Consider an example where the list
elements are Integer objects with values in the range 0-99. In
this case, it's possible to write a local version of toString
that is faster than the standard method. The code looks like
this:
import java.util.*;
public class PerfDemo {
// length of ArrayList objects
static final int MAXLISTLEN = 10000;
// maximum value in Integer object
static final int MAXNUM = 99;
// cache of formatted strings for small integers
static String cache[] = new String[MAXNUM + 1];
// fill cache at program startup
static {
for (int i = 0; i <= MAXNUM; i++) {
cache[i] = Integer.toString(i);
}
}
// local version of toString, that uses the cache
static String localtoString(List list) {
StringBuffer sb = new StringBuffer();
sb.append("[");
int size = list.size();
for (int i = 0; i < size; i++) {
Integer iobj = (Integer)list.get(i);
sb.append(cache[iobj.intValue()]);
if (i + 1 < size) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
public static void main(String args[]) {
Random rn = new Random(0);
List list = new ArrayList();
// fill list with random Integer values 0-99
for (int i = 1; i <= MAXLISTLEN; i++) {
int r = rn.nextInt(MAXNUM + 1);
list.add(new Integer(r));
}
String s1 = null;
String s2 = null;
// do timing of standard approach