4
\$\begingroup\$

Here's some code I wrote which sends multi-dimensional arrays to a file, is there any way I can make a generic method?

public void sendSevenToFile(int[][][][][][][] seven, String filename) throws IOException {
    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(filename));
    outputStream.writeObject(seven);
}

public void sendSixToFile(int[][][][][][] six, String filename) throws IOException {
    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(filename));
    outputStream.writeObject(six);
}

public void sendThreeToFile(int[][][] three, String filename) throws IOException {
    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(filename));
    outputStream.writeObject(three);
}
\$\endgroup\$
  • 1
    \$\begingroup\$ How does your code look that calls these methods? What is the use-case for having these methods and for serializing int arrays of different dimensions? \$\endgroup\$ – Simon Forsberg Mar 26 '17 at 19:04
  • \$\begingroup\$ @SimonForsberg especially a 7-dimensional array! \$\endgroup\$ – Tamoghna Chowdhury Mar 26 '17 at 19:20
  • \$\begingroup\$ It's for pattern databases, storing a sparse index for the sliding tile puzzle @SimonForsberg \$\endgroup\$ – user3667111 Mar 26 '17 at 19:22
6
\$\begingroup\$

An array in Java is an object, not a primitive, so you can accept the parameter as Object, without specializing it as an array (you do not do that in your current code either, so you do not lose anything by this approach). Yes, you lose some type safety this way, but you can get it back during runtime by using <argument array object>.getClass().isArray(). Overall, my approach is:

/**
* Serializes an array of arbitrary dimensions to a file
* @param array    the array to serialize
* @param filename the name of the file to serialize <code>array</code> to
* @throws IllegalArgumentException if <code>array</code> is not an array
* @throws IOException              if some error was encountered during serialization
*/
public void serializeArrayToFile(Object array, String filename) throws IOException {
    if(!array.getClass().isArray()){
        throw new IllegalArgumentException("Cannot serialize non-array object " + array.toString());
    }
    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(filename));
    outputStream.writeObject(array);
}

And just in case you want this to be absolutely specific for only int arrays of arbitrary dimensions,

/**
* Serializes an array of arbitrary dimensions to a file
* @param array    the array to serialize
* @param filename the name of the file to serialize <code>array</code> to
* @throws IllegalArgumentException if <code>array</code> is not an <code>int</code> array
* @throws IOException              if some error was encountered during serialization
*/
public void serializeArrayToFile(Object array, String filename) throws IOException {
    Class<?> classOfArray = array.getClass();
    if(!classOfArray.isArray()){
        throw new IllegalArgumentException("Cannot serialize non-array object " + array.toString());
    }
    Class<?> componentType = classOfArray.getComponentType();
    if(!(componentType.isPrimitive() && 
         int.class.isAssignableFrom(componentType)) {
        // Can also be `UnsupportedOperationException1, depending on required semantics
        throw new IllegalArgumentException(String.format(
                                           "Serialization of non-`int` array %s of component type %s is not allowed", 
                                            array.toString(), componentType.toString()));
    }
    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(filename));
    outputStream.writeObject(array);
}

Note that a nice side-effect of this approach is that you can now serialize arrays of arbitrary dimensions!

Note the naming convention - I use serialize instead of send, as that is what you are actually doing. Although I do not do it here for purposes of compatibility with the existing project structure, you might consider making this method static as it does not depend on the implicit this.

Also, the first array.toString() isn't required, the string concatenation will do it implicitly, but in my policy for such cases, explicit is better than implicit.

Addendum

The OP asked in the comments as to how to determine the number of dimensions of an array if it has been casted to Object. This is possible via reflection, whether by descending the type hierarchy until the result of <array object>.getClass().isArray() is false (slow), or by counting the number of '[' characters in the result of <array object>.getClass().getName() (faster). I'll illustrate the second method, which has been suggested by @wizzwizz4 in the comments, as that is more practical in all respects (I do not consider it to be "hacky", as @wizzwizz4 does, as the result of getName() for the class of any array type is explicitly documented as here.

public static int getDimensionsOfArray(Object array) {
    return (int) array.getClass()
                                .getName()
                                .chars()
                                .filter(c -> c == '[')
                                .count();
}

I use Java 8 Streams here, but you could just as well do it with a loop (I like the declarative approach of using Streams, as my primary language is Scala). The cast to int exists because Stream.count() returns a long, as Streams are lazy and can theoretically hold an infinite number of elements.

References:

I referred to this StackOverflow answer for the array type verification code.

\$\endgroup\$
  • 1
    \$\begingroup\$ When reading these files back in, how do I determine which ones have n-dimensions? \$\endgroup\$ – user3667111 Mar 26 '17 at 19:51
  • 1
    \$\begingroup\$ @user3667111 You could do it by counting the number of [ marks at the beginning... but that seems a bit hackish. \$\endgroup\$ – wizzwizz4 Mar 26 '17 at 19:56
  • \$\begingroup\$ @user3667111 I'd recommend storing the information alongside it \$\endgroup\$ – Nic Hartley Mar 26 '17 at 20:42

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.