Многомерные массивы

После знакомства с одномерными массивами пришло время познакомиться с многомерными. В математических вычислениях часто используются матрицы — двумерные массивы. Также можно представить кубические (трехмерные) массивы. Но на самом деле это представление не совсем верное с точки зрения организации массивов в Java. Для начала познакомимся с формой записи двумерного массива а потом уже более глубоко покопаемся в том, как на самом деле устроен двумерный массив. Итак, вариант записи достаточно простой:

Мы объявили двумерный массив символов и создали объект, который можно использовать. Как видите он не сильно отличается от того, что мы видели раньше при знакомстве с одномерными массивами. Прежде чем мы углубимся (как я обещал) в устройство двумерного массива, напишем несложный пример, который заполнит массив символов определенным рисунком, а потом выведет его на экран.
Для начала сделаем простое заполнение массива одним символом — например ‘#’.

Рассмотрим подробнее два двойных цикла (мы кстати сними уже встречались при рисовании фигур в разделе Управление порядком операций. Первый двойной цикл позволяет нам с помощью переменных i и j пройти по всем индексам массива graph и каждому его элементы присвоить значение ‘#’. Вот и вся его задача — пройтись по каждому элементу путем обращения к нужному индексу. Никаких облегчающих конструкций в Java пока нет (есть такое понятие как «замыкание», но они ожидаются в Java 8 в середине 2013 года. Вполне допускаю, что я не успею закончить курс до ее выхода и Вы сможете сказать о моих статьях что-то вроде «старье» :)).
Второй цикл предназначен для вывода массива на экран. Эту конструкцию мы тоже уже видели — в нем первый цикл отвечает просто за переход на следующую строку. А внутренний печатает одну строку без каких-либо переходов. Думаю. что Вы сможете разобраться.
Следующий пример заполняет массив символов более сложной фигурой — квадратом с пустым содержанием. Символ ‘#’ встречается здесь только по самому краю. Весь алгоритм заполнения содержится в первом цикле. условие достаточно простое — если индекс является либо первым равным 0) либо последним (равен SIZE-1), то используем символ ‘#’. Иначе — символ пробела ‘ ‘.

Думаю, что Вы сможете разобраться в этом примере самостоятельно. А после этого примера предлагаю Вам попробовать «нарисовать» фигуры, которые Вы возможно уже пробовали создавать — треугольники, ромбы и прочая. Я же хотел предложить Вам посмотреть на многомерные массивы более глубоко.

Двумерность как массивы массивов

Надеясь на то, что Вы попробовали порисовать фигуры и получили четкое понимание, что сами массивы вещь достаточно несложная — сложны именно алгоритмы, которые используют массивы для своей реализации, предлагаю посмотреть как устроены многомерные массивы.На самом деле двумерный массив не является матрицей — это массив массивов. Т.е. Вы создаете массив, внутри которого находятся указатели на одномерные массивы — массив массивов. Я могу конечно еще раз повторить эти слова, но думаю, что это вряд ли поможет, если Вы еще не поняли эту идею. Попробуйте рассмотреть это на примере кода, где написаны комментарии.

Как видите в начале програмы мы создаем массив массивов — он имеет размер SIZE, но внутри его элементы указывают на null. Первый цикл нам это может продемонстрировать — каждый элемент graph[i]равен null.
Второй цикл создает массивы случайной размерности от 25 до 75 элементов. И вот уже эти элементы и есть символы. Для создания случайного числа используется специализированный класс Math. Метод random() создает случайное число от 0 до 1, которые мы умножаем на 50 (получаем случайное число от 0 до 50) и округляем его с помощью второй функции round. К полученному целому числу прибавляем 25, тем самым сдвигая диапазон на 25 — от 25 до 75. Последним шагом является приведение полученного числа из типа long к типу int — размер массива использует тип int. Вторая строка уже создает массив случайного размера. Третий цикл печатает размеры массивов. Таким образом на самом деле мы создаем не матрицу, а массив массивов.
После недолгих размышлений Вы поймете, что трехмерный массив на самом деле это массив массивов массивов — т.е. массив, который содержит ссылки на массивы, которые уже в свою очередь содержат настоящие элементы (только учтите, что если элементами массивов являются классы, то конечные элементы без инициализации будут тоже ссылки на null).

Упрощенная конструкция инициализации массива

В заключении я хочу показать Вам конструкцию, которая позволяет присвоить элементам массива значения без сложных циклов. Не буду придумывать слова для объяснения — просто приведу пример.

Как видите, все достаточно просто — вы перечисляете внутри фигурных скобок нужные Вам значения. В случае, если нужен многомерный массив, для каждого массива внутри массива тоже надо открыть (и потом закрыть) фигурные скобки. В случае, если нужны объекты, то Вы можете вызывать конструкторы прямо внутри скобок. Например для создания массива из трех роботов можно сделать так:

На этом мы можем закончить знакомство с массивами. Я показал Вам вполне достаточно для того, чтобы Вы могли использовать этот инструмент в своих программах. Теперь дело за малым — надо начать писать программы и принимать решение надо ли Вам использовать массив в данной программе или нет. И если Вы сочтете это нужным сделать — небольшой справочник у Вас уже есть. Примеры из данной статьи доступны для скачивания: MultiArray1MultiArray2MultiArray3ArrayInit

Самостоятельно

В качестве самостоятельной работы можете попробовать сделать несколько заданий, которые когда-то я сам делал и мне они понравились. Для их выполнения я хочу предложить вам познакомится с конструкцией, которая позволяет выводить на экран числа в определенном формате. Если вас заинтересует более подробная информация по способам форматирования, можете посмотреть их сами (правда на английском языке) прямо в документации по Java — Formatter

Например, я хочу вывести число 12 и чтобы оно занимало не 2 позиции, а 4.
Т.е. не так

А вот так

Как видите перед 12 есть еще 2 пробела. Для такого вывода используется конструкция

Эта конструкция содержит внутри скобок два аргумента, Второй — это наше число 12. А вот первый гораздо интереснее — эта строка говорит о том, как именно я хочу вывести число 12 (а точнее, аргумент, который идет после строки форматирования).
Сначала идет «%4d» — это значит, что я вывожу целое число и это число (даже если оно занимает 2 символа) будет как минимум занимать 4 символа с ведущими пробелами в случае, если число цифр в нем меньше четырех.
Набор «\r\n» означает, что курсор перейдет на следующую строку. Хотя часто достаточно либо \r либо \n — это уже зависит от операционной системы, под которой вы работаете.
Что еще более интересно, вы можете использовать несколько аргументов (Java с 1.5 позволяет это). Мы пока не рассматривали эту возможность. но обязательно упомянем о ней. Для печати нескольких чисел можно написать так

Думаю, что вы догадались, что вывод будет вот такой:

  99 и 88

Думаю, что этой информации будет достаточно.

1. В качестве тренировки создайте массив 5 на 5, заполните его числами от 1 до 25 и выведите его на экрана чтобы каждое число занимало 3 позиции. Если разобрались с выводом, то тогда задания посложнее.

2. Заполните массив числами, которые увеличиваются на 1 по спирали. Для примера массив 4 на 4 должен выглядеть вот так

3. Заполните массив «ходом коня» — надеюсь вы знаете как ходит конь в шахматах (буквой Г). Так вот существует простой алгоритм, который позволяет гарантированно заполнить доску ходом коня размерами от 5 до 70 (квадратную конечно). Т.е. сначала заполните массив числом 0, а потом на первой клетке (элементе массива) ставится число 1, на следующей, на которую прыгает конь — 2 и так до тех пор, пока не останется клеток, на которые конь не ступал. Если остались нулевые значения — значит что-то не так.
Алгоритм достаточно простой — надо прыгать на ту клетку, с которой будет меньше всего ходов.

В следующих статьях мы продолжим изучение конструкций для языка Java.

Один из ответов (на вопрос 2) — Спасибо пользователю Grif.

И теперь нас ждет следующая статья: Абстрактные классы.