Сколько объектов создается при инициализации двумерного массива в Java и как они хранятся в памяти
Что проверяют
Понимаете ли вы, что многомерного массива как единой сущности в Java нет: есть массив ссылок на массивы. От этого зависит и счёт объектов, и расход памяти, а для Android это не абстракция, а живые мегабайты и паузы сборщика.
Как отвечать
Считайте по строчке объявления. Выражение new int[3][4] создаёт четыре объекта: один внешний массив на три ссылки и три отдельных массива по четыре int. Формула простая: единица плюс количество строк. Элементы примитивного типа объектами не являются, они лежат внутри своей строки.
Если написать new int[3][], объект будет ровно один: внешний массив из трёх ссылок null, строки создаются потом и могут быть разной длины. Для Integer[3][4] объектов уже минимум четыре плюс до двенадцати боксов, и это главный аргумент против обёрток в горячем коде.
В памяти каждый из этих объектов лежит в куче отдельно, со своим заголовком и полем длины. Непрерывным блоком, как в C, двумерный массив не хранится: строки могут оказаться в разных местах кучи, и обход по столбцам промахивается мимо кэша.
Что из этого следует на практике:
- для матрицы фиксированной ширины дешевле один плоский массив и индекс
y * width + x; - разреженные данные с целочисленным ключом лучше ложатся в
SparseArray, чем в матрицу; - обходить надо построчно, внешний индекс во внешнем цикле.
Где спотыкаются
Называют число элементов. На new int[3][4] отвечают «двенадцать объектов» или «тринадцать», считая объектом каждый int. Примитив объектом не становится, его негде хранить отдельно.
Путают new int[3][4] и new int[3][]. Во втором случае строк ещё нет, и первое же обращение к элементу даёт NullPointerException.
Считают, что копирование внешнего массива копирует данные. И clone(), и Arrays.copyOf поверхностные: новый внешний массив ссылается на те же строки, и правка элемента видна в обеих копиях. Нужен обход с копированием каждой строки.