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

После знакомства с одномерными массивами пришло время познакомиться с многомерными. В математических вычислениях часто используются матрицы — двумерные массивы. Также можно представить кубические (трехмерные) массивы. Но на самом деле это представление не совсем верное с точки зрения организации массивов в 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.

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

  • Grif  says:

    Не знаю причин … но часть программы при копировании исчезает …

    • Grif  says:

      Отправил в теле письма и на всякий случай в виде файла RTF.

      • admin  says:

        Опубликовал в статье

  • Grif  says:

    Здравствуйте!
    Сейчас в свободное работаю над решением задачи №3 «Ход конём», стараюсь применить принципы объектного программирования и вот столкнулся с затруднением.
    Подскажите пожалуйста почему конструкция типа:

    public class Horse {
    public static void main(String[] args) {
    int Size = 10;
    int [] Array = new int [Size];
    for (int i = 0; i<Size; i++){Array[i]=0;}}}

    работает

    а конструкция типа:

    public class Horse {
    private int Size;
    private int [] Array = new int [Size];
    public Horse (int Size){
    this.Size = Size;
    for (int i = 0; i<Size; i++){this.Array[i]=0;}}}

    выдаёт ошибку в "{this.Array[i]=0;}"?

    • admin  says:

      Честно говоря я здесь про ООП не думал — тут на мой взгляд проще делать обычный процедурный вариант. Сделать фнукцию, которая может посчитать количество свободных шагов с любого поля и уже ее использовать для реализации алгоритма. Объекты тут как-то странно выглядят. Разве что для отображения процесса заполнения можно что-то придумать.

  • Grif  says:

    Я решил задачу №3 “Ход конём” при помощи ООП, у меня получилось четыре класса:
    1-Класс Конь (принимает текущие координаты коня и рассчитывает возможные ходы без ограничений);
    2-Класс Поле (создает поле по заданной ширине и длине на Поле остаются ходы Которые совершил Конь);
    3-Класс Жокей (Управляет конем на поле сопоставляет размеры участка поля (загона) выделенного для коня, стиль езды коня и рассчитывает путь в зависимости от задачи.)
    4-Класс Менеджер (тут и так все ясно).
    Мой конь умеет заполнять не только квадратные поля но и прямоугольные, если поле заполнено не полностью Жокей дает отчет о последней остановке коня.
    Хотелось бы поделиться решением с читателями, но весь код с комментариями занимает около 350 строчек, вряд ли мне удастся выложить его сюда. Если администратор согласится, то я вышлю файлы решения а он прикрепит их к сайту для желающих.

    • admin  says:

      Администратор согласится 🙂 Высылайте

      • Grif  says:

        Спасибо, выслал на почту 🙂

  • Grif  says:

    Интересно, что немного доработав код, я дал задачу менеджеру проанализировать все массивы размером от 0Х0 до 100Х100 (квадратных и прямоугольных) и за 1,5 минуты (у меня моноблок на базе нетбучного двухъядерника т.е. слабенький ПК) получил ответ : Jokey — kon vypolnit zadachu dlya 8784 massivov razmerom ot 0X0 do 100X100.

    • admin  says:

      О, уже правильно думаете — «дал задачу менеджеру». Каждый класс занимается своим делом.
      Скорость мне сложно оценить. Но при вычислениях Java достаточно неплохо работает.

      • Grif  says:

        О … там было очень много вычислений (обработано 10000 массивов различной величины при этом для каждой ячейки проводился анализ на 8 возможных ходов и для каждого возможного ещё столько же и т.д.) т.е. учитывая, что мой ПК имеет по части вычислений уровень ПК 2004 года, то представленный результат весьма не плох.

  • Екатерина  says:

    public class MultiArray
    {
    public static void main(String[] args) {
    // Объявим константу для размера массива
    int SIZE = 8;
    // Создаем двумерный массив
    int[][] graph = new int[SIZE][SIZE];
    // Объявляем начальное значение элемента в массиве
    int n=1;
    // Объявляем шаг уменьшения квадрата
    int k=1;
    for (int i=0; i<SIZE/2;i++)
    {
    for (int j=i; j<=SIZE-k;j++)
    {
    graph[i][j]=n;
    n++;
    if (j==SIZE-k){
    for (int m=k; m=k;j—) {
    graph[SIZE-k][j]=n;
    n++;
    if (j==k)
    for(int l=SIZE-k;l>=k;l—) {
    graph[l][j-1]=n;
    n++;
    }
    }

    k++;
    }
    // Теперь выводим массив на экран
    for (int i = 0; i < SIZE; i++) {
    for (int j = 0; j < SIZE; j++) {
    System.out.format("%3d",graph[i][j]);
    }
    System.out.println();
    }

    }
    }

    • admin  says:

      У меня не получилось это запустить. Лучше код оборачивать тэгами
      <pre>
      код здесь
      </pre>

  • Nibbler  says:

    Решил задачу «ход конем» — здесь не стал выкладывать, чтобы не загромождать комментарии. Отправил решение на почту. Буду очень благодарен за критическую оценку. У меня получилось 3 класса: 1) Основной: GeHorse.java; 2) Конь: Horse.java; 3) Шахматная доска: ChessDesk.java
    Доска определяет естественные границы возможных перемещений коня и запоминает «карту сделанных ходов» коня.
    Конь обладает собственной «конской логикой» и принимает решение, куда ему дальше идти, исходя из своих функциональных возможностей (только буквой «Г»), а также, руководствуясь принципом поиска клетки, откуда было бы минимальное количество ходов и ограничениями и состоянием доски.
    Сначала поставил себе цель решить задачу с помощью обычного процедурного подхода, но сами-собой напрашивались методы и я, также, пошел по стопам Grif. Конструктор доски позволяет создавать доску любой размерности, однако, форматирование вывода сейчас настроено под доски размерности не больше 30 (900 клеток). Коня можно устанавливать (при его создании) на любую из клеток доски — задача будет решена в любом случае. Метод «минимума ходов», как выяснилось работает только для досок четной размерности (8, 10, 12 и т.п.). Для досок нечетной размерности задача решения не имеет — остаются незанятые клетки.

    • admin  says:

      Я получил письмо — постараюсь посмотреть. Удачи.

  • Firefly  says:

    Скажите пожалуйста, для форматированного вывода ведь еще подойдет System.out.printf()?
    А в чем существенное отличие этих команд?

    • admin  says:

      Для форматированного вывода он и предназначен.
      Что касается отличия — о каких командах идет речь ?

      • Firefly  says:

        System.out.printf() и System.out.format()

        • admin  says:

          Возможность передавать неопределенное число параметров появилась только в Java 1.5. До этого времени использование printf было по сути невозможно. Вот и не было его — было println — не самая удачная поделка.

  • Oleh  says:

  • Creed  says:


    package my.array;

    public class Spiral {
    private int size;
    private int arr[][];

    public Spiral(int size) {
    this.size = size;
    arr = new int[size][size];
    }

    public void CreateSpiral (){
    int num=1;
    int left=0;
    int right = size-1;
    int top=0;
    int bottom = size-1;

    while ( num <= size*size){
    for (int i = left; i<=right;i++)
    arr[top][i] = num++;
    for (int j = top+1; j=left;i--)
    arr[bottom][i] = num++;
    for (int j =bottom-1;j>top;j--)
    arr[j][left] = num++;
    left++;
    right--;
    top++;
    bottom--;
    }
    }

    public void PrintSpiral (){
    for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++)
    System.out.format("%4d", arr[i][j]);
    System.out.println();
    }
    }
    }

  • Денис  says:

    Заполняет по спирали массивы любой размеренности (кроме одномерных)

    • Денис  says:

      код класса Configuration

  • Oleg  says:

    Что-то вы слишком много всего намудрили. Про массивы в Java на http://proglang.su/java/142 проще написано.

    • admin  says:

      Я не мудрил — мне показалось интересно и важно рассказать так, как я это сделал. Но на вкус и цвет….

  • v  says:

    Задание1 разными вариантами:

  • v  says:

    Вариант с двумерным массивом

  • K7gt  says:

    Вариант решения 2ой задачи.
    Вроде работает…

Leave a reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.