Существует ли разница на уровне низкого уровня между int [large] [small] или int [small] [large] в Java?

Этот вопрос, вероятно, потребует ответа некоторых компиляторов. В настоящее время я работаю над проектом, где я буду создавать массив, который может быть

int[2][veryLargeNumber]

или

int [veryLargeNumber][2]

Это не имеет никакого отношения к логике, но я думал, что форма (и, следовательно, размер) в памяти может отличаться (может быть, вопрос должен быть, являются ли компиляторы достаточно умными, чтобы переупорядочить массивы в соответствии с ними)?

Ответ 1

Java фактически реализует только одномерные массивы. Он имеет многомерные типы, однако двумерные массивы фактически реализуются как массив массивов. Каждый массив имеет накладные расходы около 16 байт. Вам лучше с int[2][x] минимизировать накладные расходы.

Вы можете полностью избежать этой проблемы, используя вспомогательные методы.

final int[] array = new int[2 * veryLargeNumber];

public int get(int x, int y) {
    return array[idx(x, y)];
}

public void set(int x, int y, int val) {
    array[idx(x, y)] = val;
}

private int idx(int x, int y) {
    return x * 2 + y; // or x * veryLargeNumber + y;
}

Чтобы обеспечить это для себя, каждый объект hash уникален, генерирует hashCode, который хранится в его заголовке Object.

Вы можете видеть из http://ideone.com/oGbDJ0, что каждый вложенный массив является само по себе.

int[][] array = new int[20][2];
for (int[] arr : array) {
    System.out.println(arr);
}

печатает внутреннее представление int[], которое [I, за которым следует @, за которым следует hashCode(), хранящийся в заголовке. Это не так, как считают некоторые, адрес объекта. Адрес не может использоваться как hashCode, поскольку объект может быть перемещен в любое время GC (если у вас нет JVM, который никогда не перемещает объекты)

[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]
[[email protected]

Вы можете видеть, сколько памяти используется, если вы отключите TLAB с помощью -XX:-UseTLAB https://github.com/peter-lawrey/Performance-Examples/blob/master/src/main/java/vanilla/java/memory/ArrayAllocationMain.java

public static void main(String[] args) {

    long used1 = memoryUsed();
    int[][] array = new int[200][2];

    long used2 = memoryUsed();
    int[][] array2 = new int[2][200];

    long used3 = memoryUsed();
    if (used1 == used2) {
        System.err.println("You need to turn off the TLAB with -XX:-UseTLAB");
    } else {
        System.out.printf("Space used by int[200][2] is " + (used2 - used1) + " bytes%n");
        System.out.printf("Space used by int[2][200] is " + (used3 - used2) + " bytes%n");
    }
}

public static long memoryUsed() {
    Runtime rt = Runtime.getRuntime();
    return rt.totalMemory() - rt.freeMemory();
}

печатает

Space used by int[200][2] is 5720 bytes
Space used by int[2][200] is 1656 bytes

Ответ 2

Интересный вопрос, я запустил простую программу

int N = 100000000;
long start = System.currentTimeMillis();
int[][] a = new int[2][N];
System.out.println(System.currentTimeMillis() - start + " ms");

В результате получилось 160 ms. Затем я запустил другой вариант

int N = 100000000;
long start = System.currentTimeMillis();
int[][] a = new int[N][2];
System.out.println(System.currentTimeMillis() - start + " ms");

В результате получилось 30897 ms. Итак, первый вариант кажется намного лучше.

Ответ 3

 int[2][veryLargeNumber] 

создает два массива с verlarnumber элементов
в то время как

 int[veryLargeNumber][2] 

создает очень большое количество массивов, имеющих два элемента.

Примечание. При создании массива есть накладные расходы. поэтому предпочтителен первый вариант

Ответ 4

Короче говоря, int[2][veryLargeNumber] - лучший подход.

Рекомендуемый Peter (int[] array = new int[2 * veryLargeNumber];) еще лучше, или если ваша память является вашей проблемой, тогда вы можете использовать longs вместо целых чисел (long[] array = new long[veryLargeNumber];) и побитовых операторов или, еще лучше, использовать кеширование, когда это возможно.

Осторожно! Представление [[email protected] в большинстве дистрибутивов JDK/JRE (Sun и Oracle) дается System.identityHashCode(), и оно не гарантируется быть уникальным для каждого объекта. Поэтому вы не можете полагаться на System.out.println(array);, чтобы проверить единственность объекта массива.