2
\$\begingroup\$

I have this multidimensional array that generalizes vectors and matrices:

com.github.coderodde.util.MultidimensionalArray.java:

package com.github.coderodde.util;

/**
 * This class implements a multidimensional array.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Nov 13, 2022)
 * @since 1.6 (Nov 13, 2022)
 */
public final class MultidimensionalArray<E> {

    private final int[] dimensions;
    private final E[] storage;
    
    public MultidimensionalArray(int dimension1, int... otherDimensions) {
        dimensions = new int[1 + otherDimensions.length];
        
        dimensions[0] = checkDimension(dimension1);
        
        for (int i = 0; i < otherDimensions.length; i++) {
            dimensions[i + 1] = checkDimension(otherDimensions[i]);
        }
        
        storage = (E[]) new Object[multiplyDimensions()];
    }
    
    public E get(int... coordinates) {
        checkCoordinates(coordinates);
        return storage[convertCoordinatesToIndex(coordinates)];
    }
    
    public void set(E element, int... coordinates) {
        checkCoordinates(coordinates);
        storage[convertCoordinatesToIndex(coordinates)] = element;
    }
    
    private void checkCoordinates(int... coordinates) {
        if (coordinates.length < dimensions.length) {
            throw new IllegalArgumentException(
                    "The number of coordinates is too small: " 
                            + coordinates.length 
                            + ". Must be " 
                            + dimensions.length 
                            + ".");
        }
        
        if (coordinates.length > dimensions.length) {
            throw new IllegalArgumentException(
                    "The number of coordinates is too large: " 
                            + coordinates.length 
                            + ". Must be " 
                            + dimensions.length 
                            + ".");
        }
        
        for (int i = 0; i < dimensions.length; i++) {
            checkCoordinate(coordinates[i], dimensions[i]);
        }
    }
    
    private void checkCoordinate(int index, int limit) {
        if (index < 0) {
            throw new IndexOutOfBoundsException(
                    "Coordinate is negative: " 
                            + index 
                            + ". Must be at least zero (0).");
        }
        
        if (index >= limit) {
            throw new IndexOutOfBoundsException(
                    "Coordinae is too large: "
                            + index
                            + ". Must be at most "
                            + (limit - 1)
                            + ".");
        }
    }
    
    private int convertCoordinatesToIndex(int... coordinates) {
        checkCoordinates(coordinates);
        int index = 0;
        int multiplier = 1;
        
        for (int i = dimensions.length - 1; i >= 0; i--) {
            index += multiplier * coordinates[i];
            multiplier *= dimensions[i];
        }
        
        return index;
    }
    
    private int multiplyDimensions() {
        int ret = dimensions[0];
        
        for (int i = 1; i < dimensions.length; i++) {
            ret *= dimensions[i];
        }
        
        return ret;
    }
    
    private static int checkDimension(int dimensionCandidate) {
        if (dimensionCandidate < 1) {
            throw new IllegalArgumentException(
                    "Dimension too small: "
                            + dimensionCandidate
                            + ". Must be at least 1.");
        }
        
        return dimensionCandidate;
    }
}

com.github.coderodde.util.Demo.java:

package com.github.coderodde.util;

public final class Demo {
    
    public static void main(String[] args) {
        MultidimensionalArray<Integer> arr =
                new MultidimensionalArray<>(2, 3, 4);
        
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 3; j++) {
                for (int k = 0; k < 4; k++) {
                    arr.set(100 * i + 10 * j + k, i, j, k);
                }
            }
        }
        
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 3; j++) {
                for (int k = 0; k < 4; k++) {
                    System.out.println(arr.get(i, j, k));
                }
            }
        }
    }
}

com.github.coderodde.util.MultidimensionalArrayTest.java:

package com.github.coderodde.util;

import org.junit.Test;
import static org.junit.Assert.*;

public final class MultidimensionalArrayTest {
    
    @Test
    public void setAndGet() {
        MultidimensionalArray<Integer> arr = 
                new MultidimensionalArray<>(2, 3, 5);
        
        arr.set(1, 1, 2, 0);
        arr.set(2, 0, 0, 4);
        
        assertEquals(Integer.valueOf(1), arr.get(1, 2, 0));
        assertEquals(Integer.valueOf(2), arr.get(0, 0, 4));
    }
    
    @Test(expected = IllegalArgumentException.class)
    public void throwsOnTooManyCoordinates() {
        MultidimensionalArray<Integer> arr =
                new MultidimensionalArray<>(3, 2, 4, 5);
        
        arr.get(0, 1, 1, 2, 0);
    }
    
    @Test(expected = IllegalArgumentException.class)
    public void throwsOnTooLittleCoordinates() {
        MultidimensionalArray<Integer> arr =
                new MultidimensionalArray<>(3, 2, 4, 5);
        
        arr.get(0, 1, 1);
    }
    
    @Test(expected = IndexOutOfBoundsException.class)
    public void throwsOnTooSmallCoordinate() {
        MultidimensionalArray<Integer> arr =
                new MultidimensionalArray<>(3, 2, 4, 5);
        
        arr.get(0, 1, 1, -1);
    }
    
    @Test(expected = IndexOutOfBoundsException.class)
    public void throwsOnTooLargeCoordinate() {
        MultidimensionalArray<Integer> arr =
                new MultidimensionalArray<>(3, 2, 4, 5);
        
        arr.get(0, 2, 1, 0);
    }
}

Critique request

As always, I would like to hear anything that comes to mind.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$
  • I am missing exposed information about dimension sizes.
  • multiplyDimensions could be just called length, possibly public too, although it would be redundant information if you expose dimension sizes
  • Cool thing to have would be to pass initializer function to constructor - function, that accepts coordinates of all dimensions and returns instance of the object. Then constructor loops over the whole array and fills it using this function.
  • That leads to possibility of creating immutable version of this class, missing set method.
  • I would consider using functional approach and replacing foreaches (not the multi-dimensinal ones), some of your cycle code would be a lot nicer and shorter.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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