How to get the sum of two n-dimensional arrays in Java?

13,452

Solution 1

Here is complete and simple solution. You can pass any dimension arrays to copyArray method.

package com.azry.test;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

public class MultiDimArray {

    public Object copyArray(Object srcArray1, Object srcArray2) {

        ArrayList<Integer> dimensions = new ArrayList<Integer>();
        getDimensions(srcArray1, dimensions);
        int[] dims = new int[dimensions.size()];
        for (int i = 0; i < dims.length; i++) {
            dims[i] = dimensions.get(i);
        }

        Object dstArray = Array.newInstance(int.class, dims);
        copyArray(srcArray1, srcArray2, dstArray);
        return dstArray;
    }

    public void copyArray(Object srcArray1, Object srcArray2, Object dstArray) {
        if (srcArray1 != null && srcArray1.getClass().isArray()) {
            if (srcArray1 instanceof int[]) {
                int[] s1 = (int[])srcArray1;
                int[] s2 = (int[])srcArray2;
                int[] d = (int[])dstArray;
                for (int i = 0; i < s1.length; i++) {
                    d[i] = s1[i] + s2[i];
                }
            }
            for (int i = 0; i < Array.getLength(srcArray1); i++) {
                copyArray(Array.get(srcArray1, i), Array.get(srcArray2, i), Array.get(dstArray, i));
            }
        }
    }

    public void getDimensions(Object array, List<Integer> dimensions) {
        if (array != null && array.getClass().isArray()) {
            dimensions.add(Array.getLength(array));
            if (Array.getLength(array) > 0) {
                getDimensions(Array.get(array, 0), dimensions);
            }
        }
    }

    public static void main(String[] args) {

        int[][][] srcArray1 = new int[2][3][4];
        for (int i = 0; i < srcArray1.length; i++) {
            for (int j = 0; j < srcArray1[i].length; j++) {
                for (int k = 0; k < srcArray1[i][j].length; k++) {
                    srcArray1[i][j][k] = 2;
                }
            }
        }

        int[][][] srcArray2 = new int[2][3][4];
        for (int i = 0; i < srcArray2.length; i++) {
            for (int j = 0; j < srcArray2[i].length; j++) {
                for (int k = 0; k < srcArray2[i][j].length; k++) {
                    srcArray2[i][j][k] = 3;
                }
            }
        }

        int[][][] dstArray = (int[][][])new MultiDimArray().copyArray(srcArray1, srcArray2);

        for (int i = 0; i < dstArray.length; i++) {
            for (int j = 0; j < dstArray[i].length; j++) {
                for (int k = 0; k < dstArray[i][j].length; k++) {
                    System.out.println("[" + i + "," + j + "," + k + "] = " + dstArray[i][j][k]);
                }
            }
        }
    }
}

Solution 2

The problem is that you cannot treat arrays like you are currently doing: You receive Object vectorN1 in addVectorsN(...) that is actually (Object)matrice1, so int[][]. But then, you access it like (int)vectorN1, which is wrong, because it's actually an array, not an int. So, you should access to vectorN1 like this: int i = ...; vectorN1[i]; and I would then keep vectorN1 as int[][] instead of converting it into an Object. And I would do the same for vectorN2 as well.

EDIT:

I would change

addVectorsN( (Object)matrice1, (Object)matrice2, 2, 5 ) ) to be

addVectorsN( matrice1, matrice2, 2, 5 ) )

and you are accessing it like (int)vectorN1 in

public static Object addVectorsN( Object vectorN1, Object vectorN2, 
    int dimension, int innerlength )
{
    if ( dimension == 0 )
    {
        return (int)vectorN1 + (int)vectorN2; (...) 

which I would change to be:

public static Object addVectorsN(int[][] vectorN1, int[][] vectorN2, 
    int dimension, int innerlength )

EDIT2:

and maybe you need something like this:

    if (vectorN1.length == 1 && vectorN2.length == 1)
    {
        if (vectorN1[0].length == 1  && vectorN2[0].length == 1)
        {
            return vectorN1[0][0] + vectorN2[0][0];
        }
    }

Solution 3

This will literally do what you are asking:

public static Object sumArbitraryDimensions(Object arrayOne, Object arrayTwo) {
    if (!arrayOne.getClass().equals(arrayTwo.getClass()) ||
            !arrayOne.getClass().isArray()) {
        throw new IllegalArgumentException("not an array");
    }

    int len = Array.getLength(arrayOne);
    if (len != Array.getLength(arrayTwo)) {
        throw new IllegalArgumentException("incomparable length");
    }

    Object sum;

    if (arrayOne instanceof Object[]) {
        sum = new Object[len];

        for (int i = 0; i < len; i++) {
            ((Object[])sum)[i] = sumArbitraryDimensions(
                    Array.get(arrayOne, i), Array.get(arrayTwo, i));
        }

    } else if (arrayOne instanceof byte[]) {
        sum = new byte[len];

        for (int i = 0; i < len; i++) {
            ((byte[])sum)[i] = (byte)(((byte[])arrayOne)[i] + ((byte[])arrayTwo)[i]);
        }

    } else if (arrayOne instanceof short[]) {
        sum = new short[len];

        for (int i = 0; i < len; i++) {
            ((short[])sum)[i] = (short)(((short[])arrayOne)[i] + ((short[])arrayTwo)[i]);
        }

    } else if (arrayOne instanceof int[]) {
        sum = new int[len];

        for (int i = 0; i < len; i++) {
            ((int[])sum)[i] = ((int[])arrayOne)[i] + ((int[])arrayTwo)[i];
        }

    } else if (arrayOne instanceof long[]) {
        sum = new long[len];

        for (int i = 0; i < len; i++) {
            ((long[])sum)[i] = ((long[])arrayOne)[i] + ((long[])arrayTwo)[i];
        }

    } else if (arrayOne instanceof float[]) {
        sum = new float[len];

        for (int i = 0; i < len; i++) {
            ((float[])sum)[i] = ((float[])arrayOne)[i] + ((float[])arrayTwo)[i];
        }

    } else if (arrayOne instanceof double[]) {
        sum = new double[len];

        for (int i = 0; i < len; i++) {
            ((double[])sum)[i] = ((double[])arrayOne)[i] + ((double[])arrayTwo)[i];
        }

    } else {
        throw new IllegalArgumentException("cannot sum, non-numerical");
    }

    return sum;
}

Or the slightly less verbose but less concise:

public static Object sumArbitraryDimensions(Object arrayOne, Object arrayTwo) {
    // exceptions unchecked

    int len = Array.getLength(arrayOne);

    if (arrayOne instanceof Object[]) {
        Object[] sum = new Object[len];

        for (int i = 0; i < len; i++) {
            ((Object[])sum)[i] = sumArbitraryDimensions(
                    Array.get(arrayOne, i), Array.get(arrayTwo, i));
        }

        return sum;

    } else {
        Double[] sum = new Double[len];

        // works for any numerical primitive type because
        // getDouble will perform a widening conversion
        for (int i = 0; i < len; i++) {
            sum[i] = Array.getDouble(arrayOne, i) + Array.getDouble(arrayTwo, i);
        }

        return sum;
    }
}

Or if you really want to get down to the element level that is possible:

public static Object sumArbitraryDimensions(Object objectOne, Object objectTwo) {
    if (!arrayOne.getClass().equals(arrayTwo.getClass())) {
        throw new IllegalArgumentException("incomparable types");
    }

    // reflection cannot retrieve primitive types
    // here the returned arrays will be boxed

    if (objectOne.getClass().isArray()) {
        int len = Array.getLength(objectOne);

        if (len != Array.getLength(objectTwo)) {
            throw new IllegalArgumentException("incomparable lengths");
        }

        Object[] sum = new Object[len];

        for (int i = 0; i < len; i++) {
            sum[i] = sumArbitraryDimensions(
                    Array.get(arrayOne, i), Array.get(arrayTwo, i));
        }

        return sum;

    } else if (objectOne instanceof Byte) {
        return (byte)((Byte)objectOne + (Byte)objectTwo));

    } else if (objectOne instanceof Short) {
        return (short)((Short)objectOne + (Short)objectTwo));

    } else if (objectOne instanceof Integer) {
        return (Integer)objectOne + (Integer)objectTwo);

    } else if (objectOne instanceof Long) {
        return (Long)objectOne + (Long)objectTwo);

    } else if (objectOne instanceof Float) {
        return (Float)objectOne + (Float)objectTwo);

    } else if (objectOne instanceof Double) {
        return (Double)objectOne + (Double)objectTwo);

    } else {
        throw new IllegalArgumentException("cannot sum, non-numerical");
    }
}

All of these are going to be very slow compared to regular processing because of the all the examination. I would personally recommend against doing this in any way shape or form. I don't see the use in it. Just write overloaded methods up to N dimensions:

public static int[] sumArrays(int[] arrayOne, int[] arrayTwo) {
    assert arrayOne != null && arrayTwo != null && arrayOne.length == arrayTwo.length;

    int[] sum = new int[Math.min(arrayOne.length, arrayTwo.length)];

    for (int i = 0; i < sum.length; i++) {
        sum[i] = arrayOne[i] + arrayTwo[i];
    }
    return sum;
}

public static int[][] sumArrays(int[][] arrayOne, int[][] arrayTwo) {
    assert arrayOne != null && arrayTwo != null && arrayOne.length == arrayTwo.length;

    int[][] sum = new int[Math.min(arrayOne.length, arrayTwo.length)][];

    for (int i = 0, k; i < sum.length; i++) {
        assert arrayOne[i] != null && arrayTwo[i] != null && arrayOne[i].length == arrayTwo[i].length;

        sum[i] = new int[Math.min(arrayOne[i].length, arrayTwo[i].length)];

        for (k = 0; k < sum[i].length; k++) {
            sum[i][k] = arrayOne[i][k] + arrayTwo[i][k];
        }
    }
    return sum;
}

public static int[][][] sumArrays(int[][][] arrayOne, int[][][] arrayTwo) {
    assert arrayOne != null && arrayTwo != null && arrayOne.length == arrayTwo.length;

    int[][][] sum = new int[Math.min(arrayOne.length, arrayTwo.length)][][];

    for (int i = 0, k, h; i < sum.length; i++) {
        assert arrayOne[i] != null && arrayTwo[i] != null && arrayOne[i].length == arrayTwo[i].length;

        sum[i] = new int[Math.min(arrayOne[i].length, arrayTwo[i].length)][];

        for (k = 0; k < sum[i].length; k++) {
            assert arrayOne[i][k] != null && arrayTwo[i][k] != null && arrayOne[i][k].length == arrayTwo[i][k].length;

            sum[i][k] = new int[Math.min(arrayOne[i][k].length, arrayTwo[i][k].length)];

            for (h = 0; h < sum[i][k].length; h++) {
                sum[i][k][h] = arrayOne[i][k][h] + arrayTwo[i][k][h];
            }
        }
    }
    return sum;
}
Share:
13,452
John
Author by

John

Updated on June 21, 2022

Comments

  • John
    John almost 2 years

    Adding two n-dimensional arrays in Java

    addVectors

    Two 1-dimensional arrays / vectors in Java can be added like this:

    public static int[] addVectors( int[] a, int[] b )
    {
        int[] c = new int[a.length];
    
        for ( int i = 0; i < a.length; i++ )
        {
            c[i] = a[i] + b[i];
        }
    
        return c;
    }
    

    addMatrices

    Two 2-dimensional arrays / matrices in Java can be added like this:

    public static int[][] addMatrices( int[][] a, int[][] b )
    {
        int[][] c = new int[a.length][a[0].length];
    
        for ( int i = 0; i < a.length; i++ )
        {
            c[i] = addVectors( a[i], b[i] );
        }
    
        return c;
    }
    

    Both functions require the arrays to be of the same size to avoid an arrayOutOfBoundsException.

    addArraysN

    There should be a way to add two arrays of unknown dimension using recursion.
    For example, the following code using the hypothetical function addArraysN( arr1, arr2 )

    int[][][] a = { 
                    { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } },
                    { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } },
                    { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } }
                  };
    int[][][] b = { 
                    { { 2, 2, 2 }, { 2, 2, 2 }, { 2, 2, 2 } },
                    { { 2, 2, 2 }, { 2, 2, 2 }, { 2, 2, 2 } },
                    { { 2, 2, 2 }, { 2, 2, 2 }, { 2, 2, 2 } }
                  };
    int[][][] sum = addArraysN( a, b );
    System.out.println( java.util.Arrays.deepToString( sum ) );
    

    should output

    [[[3, 3, 3], [3, 3, 3], [3, 3, 3]], [[3, 3, 3], [3, 3, 3], [3, 3, 3]], [[3, 3, 3], [3, 3, 3], [3, 3, 3]]]
    

    Now I'm wondering about how to implement this function addArraysN( arr1, arr2 ).
    I started out with the following pseudo code:

    addArraysN( arr1, arr2 )
    {
        int dimension = getDimension( arr1 );
        if ( dimension == 0 ) //there are no arrays, only numbers
            return arr1 + arr2;
        else
        {
            //create a new arrays with the same dimension and size as arr1 / arr2
            //loop through the fields with for
                //call itself when adding the fields of arr1 and arr2
            //return the sum
        }
    }
    

    New arrays can be created using the newInstance-method from java.lang.reflect.Array.
    Looping can be made possible like this:

    for ( int i = 0; i < ((int[])arr1).length; i++ )
        sum = addArraysN( ((int[])arr1)[i], ((int[])arr2)[i] );
    

    Question

    But I stumbled over a lot of runtime errors and other problems. Has anybody an idea or even a solution on how to implement this addArrayN-method?

    It should be also possible to work with ArrayList or any other Class but I'm mainly interested on how to do this with arrays... (Nevertheless if someone knows it please post!)

    Thanks in advance

    appendix 1

    My original code:

    import java.util.Arrays;
    import java.lang.reflect.Array;
    
    public class ArrayN
    {
        public static void main( String[] args )
        {
            //Vector
            int[] vector1 = {0, 1, 2, 3, 4};
            int[] vector2 = {4, 3, 2, 1, 0};
    
            int[] vector3 = ArrayN.addVectors( vector1, vector2 );
    
            for ( int num : vector3 )
            {
                System.out.print( num );
            }
            System.out.println();
    
            System.out.println();
    
            //Matrix
            int[][] matrix1 = {{0, 1, 2, 3, 4}, {4, 3, 2, 1, 0}};
            int[][] matrix2 = {{4, 3, 2, 1, 0}, {0, 1, 2, 3, 4}};
    
            int[][] matrix3 = ArrayN.addMatrices( matrix1, matrix2 );
    
            for ( int[] vector : matrix3 )
            {
                for ( int num : vector )
                {
                    System.out.print( num );
                }
                System.out.println();
            }
    
            System.out.println();
    
            //N-Array
    
            System.out.println( Arrays.deepToString( (Object[])ArrayN.addArraysN( (Object)matrix1, (Object)matrix2, 2, 5 ) ) );
    
        }
    
        public static int[] addVectors( int[] a, int[] b )
        {
            int[] c = new int[a.length];
    
            for ( int i = 0; i < a.length; i++ )
            {
                c[i] = a[i] + b[i];
            }
    
            return c;
        }
    
        public static int[][] addMatrices( int[][] a, int[][] b )
        {
            int[][] c = new int[a.length][a[0].length];
    
            for ( int i = 0; i < a.length; i++ )
            {
                c[i] = ArrayN.addVectors( a[i], b[i] );
            }
    
            return c;
        }
    
        public static Object addArraysN( Object arrayN1, Object arrayN2, int dimension, int innerlength )
        {
            if ( dimension == 0 )
            {
                return (int)arrayN1 + (int)arrayN2;
            }
            else
            {
                int[] dimensions = new int[dimension];
                for ( int i = 0; i < dimension; i++ )
                {
                    dimensions[i] = innerlength;
                }
                Object arrayN3 = Array.newInstance( Array.class, dimensions );
                for ( int i = 0; i < Array.getLength( arrayN1 ); i++ )
                {
                    Array.set( arrayN3, i, ArrayN.addArraysN( Array.get( arrayN1, i ), Array.get( arrayN2, i ), dimension-1, innerlength ) );
                }
    
                return arrayN3;
            }
        }
    }
    

    Output:

    44444
    
    44444
    44444
    
    Exception in thread "main" java.lang.IllegalArgumentException: array element type mismatch
        at java.lang.reflect.Array.set(Native Method)
        at ArrayN.addArraysN(ArrayN.java:85)
        at ArrayN.addArraysN(ArrayN.java:85)
        at ArrayN.main(ArrayN.java:41)
    

    appendix 2

    I've found the error. It was the following line:

    Object arrayN3 = Array.newInstance( Array.class, dimensions );
    

    I had to replace Array.class with int.class. The corrected line should be:

    Object arrayN3 = Array.newInstance( int.class, dimensions );
    

    Now I realized another problem the code has:
    Every array in the multidimensional array has to be of the same size because of the innerlength argument. If arrays are shorter the other values become zero:

    44444
    
    44444
    44444
    
    [[4, 4, 4, 4, 4], [4, 4, 4, 4, 4], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
    

    So I first made matrix1 and matrix2 a bit longer:

    //Matrix
    int[][] matrix1 = {{0, 1, 2, 3, 4}, {4, 3, 2, 1, 0}, {0, 1, 2, 3, 4}, {4, 3, 2, 1, 0}, {0, 1, 2, 3, 4}};
    int[][] matrix2 = {{4, 3, 2, 1, 0}, {0, 1, 2, 3, 4}, {4, 3, 2, 1, 0}, {0, 1, 2, 3, 4}, {4, 3, 2, 1, 0}};
    

    But that's not a good solution.
    Nikoloz wrote a method to find out the dimensions of arrays. Using it and another method arrayToString( Object ) I wrote my final code is now:

    import java.util.Arrays;
    import java.lang.reflect.Array;
    import java.util.List;
    import java.util.ArrayList;
    
    public class ArrayN
    {
        public static void main( String[] args )
        {
            int[][] matrix1 = {{0, 1, 2, 3, 4}, {4, 3, 2, 1, 0}};
            int[][] matrix2 = {{4, 3, 2, 1, 0}, {0, 1, 2, 3, 4}};
    
            System.out.println( ArrayN.arrayToString( ArrayN.addArraysN( matrix1, matrix2 ) ) );
    
        }
    
        public static Object addArraysN( Object arrayN1, Object arrayN2 )
        {
            ArrayList<Integer> dimensions = new ArrayList<Integer>();
            ArrayN.getDimensions( arrayN1, dimensions );
            int[] dims = new int[dimensions.size()];
            for ( int i = 0; i < dims.length; i++ )
            {
                dims[i] = dimensions.get( i );
            }
    
            if ( dims.length == 0 )
            {
                return (int)arrayN1 + (int)arrayN2;
            }
            else
            {
                Object arrayN3 = Array.newInstance( int.class, dims );
                for ( int i = 0; i < Array.getLength( arrayN1 ); i++ )
                {
                    Array.set( arrayN3, i, ArrayN.addArraysN( Array.get( arrayN1, i ), Array.get( arrayN2, i ) ) );
                }
    
                return arrayN3;
            }
        }
    
        public static void getDimensions( Object array, List<Integer> dimensions )
        {
            if ( array != null && array.getClass().isArray() )
            {
                dimensions.add( Array.getLength( array ) );
                if ( Array.getLength( array ) > 0)
                {
                    ArrayN.getDimensions( Array.get( array, 0 ), dimensions );
                }
            }
        }
    
        public static String arrayToString( Object arr )
        {
            if ( arr instanceof byte[] )
                return Arrays.toString( (byte[])arr );
            else if ( arr instanceof short[] )
                return Arrays.toString( (short[])arr );
            else if ( arr instanceof int[] )
                return Arrays.toString( (int[])arr );
            else if ( arr instanceof long[] )
                return Arrays.toString( (long[])arr );
            else if ( arr instanceof float[] )
                return Arrays.toString( (float[])arr );
            else if ( arr instanceof double[] )
                return Arrays.toString( (double[])arr );
            else if ( arr instanceof char[] )
                return Arrays.toString( (char[])arr );
            else if ( arr instanceof boolean[] )
                return Arrays.toString( (boolean[])arr );
            else
                return Arrays.deepToString( (Object[])arr );
        }
    }
    

    Another possibility would be to take dimension 1 as the base case:

    public static Object addArraysN( Object arrayN1, Object arrayN2 )
    {
        ArrayList<Integer> dimensions = new ArrayList<Integer>();
        ArrayN.getDimensions( arrayN1, dimensions );
        int[] dims = new int[dimensions.size()];
        for ( int i = 0; i < dims.length; i++ )
        {
            dims[i] = dimensions.get( i );
        }
        if ( dims.length == 1 )
        {
            Object arrayN3 = Array.newInstance( int.class, dims );
            for ( int i = 0; i < Array.getLength( arrayN1 ); i++ )
            {
                int sum = ((int[])arrayN1)[i] + ((int[])arrayN2)[i];
                Array.set( arrayN3, i, sum );
            }
            return arrayN3;
        }
        else
        {
            Object arrayN3 = Array.newInstance( int.class, dims );
            for ( int i = 0; i < Array.getLength( arrayN1 ); i++ )
            {
                Array.set( arrayN3, i, (int[])ArrayN.addArraysN( Array.get( arrayN1, i ), Array.get( arrayN2, i ) ) );
            }
    
            return arrayN3;
        }
    }
    

    Related questions

    How to sum arrays in Java
    Is it possible to dynamically build a multi-dimensional array in Java?
    Iterating over arrays by reflection
    Java Reflection - Get size of array object
    Creating an n-dimension Array in Java during runtime
    Initialising a multidimensional array in Java
    finding sum of two dimensional array java
    Adding matrices Java
    Java Matrices Arrays