3
\$\begingroup\$

I've made two symmetric methods which serialize an int array of any number of dimensions into an OutputStream, and read such an array from an InputStream. I was wondering if there is ways this code could be improved. (I have tested this code and it works correctly.) Thanks!

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;

public class IntArraySerializer {

    private IntArraySerializer() {}

    /**
     * Writes arr, which is assumed to be an n dimensional int array, to out.
     */
    public static void writeIntArr(Object arr, OutputStream out) throws IOException {
        writeInt(dimensions(arr.getClass()), out);
        writeIntArrRaw(arr, out);
    }

    /**
     * Reads an n dimensional int array from in. Symmetrical to writeIntArr.
     */
    public static Object readIntArr(InputStream in) throws IOException {
        int dimensions = readInt(in);
        return readIntArrRaw(in, dimensions - 1);
    }

    private static void writeIntArrRaw(Object obj, OutputStream out) throws IOException {
        if (obj.getClass().isArray()) {
            writeInt(Array.getLength(obj), out);
            for (int i = 0; i < Array.getLength(obj); i++) {
                writeIntArrRaw(Array.get(obj, i), out);
            }
        } else {
            writeInt((Integer) obj, out);
        }
    }

    private static Object readIntArrRaw(InputStream in, int dimensionsRemaining) throws IOException {
        int length = readInt(in);
        Object[] out = new Object[length];
        if (dimensionsRemaining == 0) {
            for (int i = 0; i < length; i++) {
                out[i] = readInt(in);
            }
        } else {
            for (int i = 0; i < length; i++) {
                out[i] = readIntArrRaw(in, dimensionsRemaining - 1);
            }
        }
        return out;
    }

    public static void writeInt(int n, OutputStream out) throws IOException {
        out.write(new byte[] {
                (byte) ((n & 0xFF000000) >> 24),
                (byte) ((n & 0x00FF0000) >> 16),
                (byte) ((n & 0x0000FF00) >> 8),
                (byte) (n & 0x000000FF)
                });
    }

    public static int readInt(InputStream in) throws IOException {
        byte[] buffer = new byte[4];
        in.read(buffer);
        return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
    }

    /**
     * @param clazz a class of array
     * @return how many dimensions clazz represents, or 0 if clazz doesn't represent an array.
     */
    public static int dimensions(Class<?> clazz) {
        if (clazz.isArray())
            return dimensions(clazz.getComponentType()) + 1;
        else
            return 0;
    }

}
\$\endgroup\$
1
\$\begingroup\$

1. writing an int to an OutputStream instance

Instead of splitting your int to bytes and putting them in a byte array, directly consume your bytes like this:

out.write((n >>> 24) & 0xFF);
out.write((n >>> 16) & 0xFF);
out.write((n >>>  8) & 0xFF);
out.write((n >>>  0) & 0xFF);

This is taken from the source code of FileOutputStream, which offers methods to write primitives to a stream.

This offers higher performance compared to packing your int in a byte array. Internally, the OutputStream.write(byte[] b) method is iterating over your byte array and calling out.write(int b) anyways. The same goes for the reading part as well I guess.

2. public helper methods

I would make readInt and writeInt private/protected. There is no need to expose them. If you plan to call them from other classes, a class called "IntArraySerializer" doesn't seem to be the correct place for them. Instead you should put them in a new class with a more expressive name like "StreamUtilities".

3. argument checking

You might want to check each argument of your public methods for null and throw an IllegalArgumentException if it is null, specifying which argument is null. This makes future debugging easier: You or other programmers won't have to look at the source code in order to debug a NullPointerException...

Throw and IllegalArgumentException in writeIntArr if the object is not an array.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.