I have a very specific technique I use for this kind of thing. It's sort of a hybrid approach that I find results in the most performant basic io code, yet maintains readability and compatibility with plain Java Serialization.
The reflection used in Java Serialization was the part that was historically thought to be slow, and it was slow. But since the addition of sun.misc.Unsafe
, that part is actually incredibly fast. There's still the initial hit of the very first call to clazz.getDeclaredFields() and other 'getDeclared' type methods of java.lang.Class, but these are cached at the VM level, so are low cost after the first (very noticeable) hit.
The remaining overhead of Java Serialization is the writing of class descriptor data; the class name, what fields it has and what types they are, etc. If java objects were xml, it would be like first writing the xsd so the structure is known, then writing the xml data without the tags. It is actually pretty performant in some situations, for example if you need to write 100+ instances of the same class type to the same stream -- you'll never really feel the hit of the class descriptor data being written the one time at the start of the stream.
But if you just need to write one instance of said class and maybe not much else, there is a way to invert things to your advantage. Instead of passing your object to the stream, which results in the class descriptor being first written followed by the actual data, pass the stream to the object and go straight to the data writing part. Bottom line is you take responsibility for the structure part in your code rather than having the ObjectOutput/ObjectInput do it.
Note, I also renamed your class from Map
to TileMap
. As BalusC points out, it's not a good class name.
import java.io.*;
public class TileMap implements Externalizable {
private String name;
private int[][] tiles;
public TileMap(String name, int[][] tiles) {
this.name = name;
this.tiles = tiles;
}
// no-arg constructor required for Externalization
public TileMap() {
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(tiles.length);
for (int x = 0; x < tiles.length; x++) {
out.writeInt(tiles[x].length);
for (int y = 0; y < tiles[x].length; y++) {
out.writeInt(tiles[x][y]);
}
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name = in.readUTF();
this.tiles = new int[in.readInt()][];
for (int x = 0; x < tiles.length; x++) {
tiles[x] = new int[in.readInt()];
for (int y = 0; y < tiles[x].length; y++) {
tiles[x][y] = in.readInt();
}
}
}
}
A write would look like this:
public static void write(TileMap tileMap, OutputStream out) throws IOException {
// creating an ObjectOutputStream costs exactly 4 bytes of overhead... nothing really
final ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(out));
// Instead of oos.writeObject(titleMap1) we do this...
tileMap.writeExternal(oos);
oos.close();
}
And a read would look like this:
public static TileMap read(InputStream in) throws IOException, ClassNotFoundException {
final ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(in));
// instantiate TileMap yourself
TileMap tileMap = new TileMap();
// again, directly call the readExternal method
tileMap.readExternal(ois);
return tileMap;
}