T.M. SoftStudio

feci quod potui, faciant meliora potentes

Купить полную версию курса "Введение в вычисления с Java" (русскоязычная версия, лекции, тесты, лабораторные работы)

Введение в вычисления с Java. Массивы, простая сортировка, многомерные массивы

Неделя 6

Лекция 29

2D Массивы

Идея одномерного массива может быть легко расширена до двумерного массива, или даже более высокой размерности массива.

Путь, с помощью которого многомерные массивы реализуются, это рассматривать двумерный массив как массив одномерных массивов, 3D массив в виде массива 2D массивов и так далее.

Сначала сосредоточимся на 2D массивах.

Итак, в 2D массиве у вас есть массив 1D массивов, то есть, для каждого элемента массива, вместо того чтобы держать данные определенного типа, каждый элемент массива представляет собой 1D массив.

Я буду использовать схему для дополнительной иллюстрации, что это значит, позже.

В общем, 2D массив имеет определенное количество строк, давайте назовем это число R, и определенное количество столбцов C, при этом R и C не должны быть одинаковыми.

Для тех, кто изучал матрицу линейной алгебры, 2D массив похож на 2D матрицу, а 1D массив, как вектор.

Но не беспокойтесь об этом, если вы не изучали матрицы раньше, потому что вам не нужно это для понимания массива.

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

Например, вы можете использовать 2D массив, чтобы представлять разные оценки, в том числе оценки за экзамены, оценки за домашние задания, оценки за лабораторные и окончательную оценку для каждого студента на курсе.

В таком представлении, каждая строка массива может представлять различные оценки, полученные студентом, и каждый столбец представляет собой различные виды оценки, скажем, столбец один для оценок за экзамены, столбец 2 для оценок за домашние работы, и так далее.

В частности, оценки всех студенты, полученные за различные работы на курсе, могут быть представлены в виде 2D массива, и для тысяч студентов, это был бы очень большой массив.

Результаты матчей в различных видах спорта также могут быть представлены в виде 2D таблиц или массивов.

Вот результаты матчей кубка мира в разных группах.

Каждая строка представляет результаты для разных команд и каждый столбец для различных атрибутов.

Коллекция из этих таблиц может быть представлена ​​в виде 3D массива.

Я уверен, что вы можете подумать о многих других примерах.

Давайте, используем результаты тестов в качестве примера для дальнейшего объяснения идеи 2D массивов.

Когда мы обсуждали 1D массив ранее, мы использовали массив оценок для хранения оценок для группы студентов.

Когда каждый студент берет несколько тестов, предполагая, что есть только четыре студентов, и каждый студент взял 4 теста, результаты могут быть представлены массивом 4х4, как этот, где каждая строка представляет результаты тестов для каждого студента, а каждый столбец представляет результаты, полученные различными студентами в том же тесте.

Массив не обязательно должен быть квадратным, так как, как правило, гораздо больше студентов, чем число тестов.

Обратите внимание, что так же, как в 1D массиве, индексы для студентов и номеров тестов начинаются с 0.

Это синтаксис объявления 2D массива. Далее этот вопрос будет обсуждаться более подробно.

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

Здесь показан синтаксис объявления 2D массива оценок типа double как переменной экземпляра для класса оценок.

Подобно 1D массиву, это объявление должно указать тип данных, которые будут храниться в массиве.

Независимо от того, какая используется размерность, массивы являются однородными по типам, то есть, содержат данные одного и того же типа.

Отличие здесь в том, что есть две пары квадратных скобок для указания размера 2D массива, по одной для каждого из 2-х измерений.

Объявление, подобно этому, просто определяет ссылочную переменную для массива, но память не была еще выделена для элементов массива.

Чтобы инициализировать массив, давайте напишем метод с названием initializeAllScores.

Также как мы сделали в 1D массиве, 2D массив создается с помощью оператора new с последующим типом данных, а затем размер каждого из измерений для 2D массива.

Это объявление выделит достаточно памяти для хранения всех элементов создаваемого массива.

Как упоминалось ранее, Java автоматически инициализирует каждый элемент массива на значение по умолчанию для соответствующего типа.

В этом случае, 0.0 используется в качестве значения по умолчанию для типа double.

Я хочу отметить, также, что, как только массив создан, размер массива является фиксированным и не может быть изменен позже в программе.

Здесь я хочу дать вам некоторую концептуальную идею о том, как этот двумерный массив представлен​​.

Когда объявление делается, ссылочная переменная scores создается.

Когда оператор new используется для создания массива, первое измерение, указаное здесь, создаст 1D массив из 4 элементов.

И каждый элемент этого 1D массива также ссылочная переменная, указывающая на другой 1D массив.

В этом примере элементы в 1D массивах синего цвета имеют тип double.

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

После того, как объявлен массив, также как и для 1D массива, можно использовать операторы присваивания, чтобы фактически инициализировать массив.

Отличается тем, что индексы для 2D массива указаны в двух парах квадратных скобок, где первый индекс это индекс строки и второй это индекс столбца.

В операторах присваивания первый индекс, 0, массива относится к первой строке массива.

Таким образом, расположение с индексами 0,0 дает значение 99.

Расположение 0,1 дает значение 89.

Расположение 0,2 дает значение 85.

Последний элемент этой строки, с индексами 0,3 дает 92.

На второй строке, первый индекс равен 1, а 2-й индекс колеблется от 0 до 3.

На 3-й строке, первый индекс 2.

Для последней строки, 4-й, обратите внимание, что индекс строки 3, что на единицу меньше, чем размер строки.

И последний элемент в этом 2D массиве индексируются 3,3.

Подобно 1D массиву, еще один способ инициализации 2D массива это сделать объявление и инициализацию в одном выражении, задав начальные значения в паре фигурных скобок.

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

Эта инициализация также определяет размер массива 4 на 4.

Подобно 1D массиву, этот формат инициализации должен быть сделан в то же время, когда объявление массива производится.

Компилятор даст вам ошибку, если это делается в отдельном выражении, после того как массив был объявлен.

3-й шаг здесь в определении класса оценок это определение метода с названием ​​getScoreByIndices для доступа к элементам в 2D массиве.

Этот метод принимает два параметра, которые определяют индексы строки и столбца элемента, чтобы быть найденным, и возвращает значение типа double потому, что это тип данных, хранящихся в массиве.

Внутри метода, мы должны сначала узнать размер массива.

В случае 1D массива, можно получить размер массива с помощью переменной экземпляра length.

Помните, что length является переменной экземпляра вместо метода, так что не содержит пару скобок после length.

В случае 2D массива, его размер определяется количеством строк, а также числом столбцов.

Так как же нам определить количество строк и количество столбцов?

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

Помните, что scores на самом деле является 1D массивом с 4-я элементами, а каждый из его элементов является другим массивом.

Таким образом, чтобы определить размер первого индекса, или количество строк, вы можете использовать scores.length.

В этом примере, scores.length даст значение 4, поскольку scores является массивом с 4-я элементами.

Теперь, как о количестве столбцов?

Как вы можете видеть, каждый элемент scores, "scores [0]", "scores [1]" и так далее это 1D массивы.

Таким образом, чтобы определить размеры этих массивов, можно использовать scores[0].length для первой строки, scores[1].length для 2-й строки и так далее.

В этом примере, все они дают то же значение 4, то есть количество столбцов.

Так вот первое выражение определяет количество строк, а затем присваивает переменной numOfRows, и второе выражение – число столбцов и присваивает переменной numOfCols.

В Java, вы на самом деле можете определить 2D массив с различным числом столбцов для разных строк, но в этом примере, будем считать, что все строки имеют одинаковое число столбцов, так что вы можете использовать scores с индексом 0 или любой другой индекс для определения количества столбцов.

Теперь, когда у нас есть размер 2D массива, мы можем проверить, являются ли индексы, определяемые в качестве параметров, в пределах диапазона.

Первое if выражение проверяет, находится ли индекс строки в пределах диапазона для числа строк.

Обратите внимание, что первое условие проверяет, является ли индекс точно меньше 0, так как 0 является допустимым индексом, и второе условие, чтобы проверить, является ли индекс больше или равен numOfRows, так как даже равно numOfRows недопустимо, потому что размер на единицу меньше, чем число строк.

И второе if выражение, чтобы проверить индекс столбца.

Значение -1 возвращается, если любой из индексов вне диапазона, предполагая, что все значения в массиве неотрицательны.

Если индекс строки и индекс столбца в пределах диапазона, значения scores, индексируемые индексом строки и индексом столбца, будут возвращены в результате для "getScoresByIndices".

Последняя часть определения scores это печать отдельных элементов в массиве scores путем обхода 2D массива, используя метод printAllScores.

Первые шаги в printAllScores это получить количество строк и количество столбцов из массива, аналогично тому, что мы сделали в предыдущем методе.

Это полная реализация метода.

Обратите внимание, что здесь есть два for цикла, или два вложенных цикла, один для управления индексом строки r и другой для индекса столбца c.

Использование вложенных циклов является довольно распространенным в обходе многомерных массивов.

Когда выполнение начинается с внешнего цикла, индекс строки r установлен в 0, и выражение IO.output распечатает номер строки в консоли.

Когда внутренний цикл выполняется, индекс столбца с будет установлен в 0.

Внутри второго for цикла, элемент массива с индексом 0, 0 будет найден с помощью метода "getScoreByIndices", который мы только что описали, и значение 99 будет получено и выведено на консоль.

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

Этот процесс будет повторяться, пока с не станет равным 3.

И цикл завершится, когда с становится равным 4.

Оператор оutputln во внутреннем цикле будет помещать строку вывода на новой линии.

Когда выполнение перейдет к началу внешнего цикла, г будет увеличена на 1, а элементы из второй строки будут получены один за другим с помощью внутреннего цикла.

Этот процесс будет продолжаться, пока последний элемент массива с индексом 3,3 не будет достигнут.

Опять же, я хочу отметить, что последний элемент этого массива индексируются 3,3, хотя размер массива составляет 4 на 4, потому что индексы начинаются с 0.

Пример работы с 2D массивом

Давайте посмотрим снова некоторые из методов, которые мы написали для 1D массивов.

Этот метод для вычисления среднего значения элементов в 1D массиве.

Когда мы имеем дело с 2D массивами, подумайте о том, что будет означать среднее для 2D массива.

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

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

Вы можете вычислить взвешенное среднее, если они имеют разные веса.

Так вот, вы хотите вычислить среднее строки.

Если вы хотите вычислить средний балл для конкретного теста, значит, вы хотите вычислить среднее столбца.

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

Этот метод "aveByRow" вычислит среднее строки 2D массива.

В то время как в предыдущем методе "aveScore" не требуются параметры, один дополнительный параметр требуется в расчете среднего строки.

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

Затем вы должны определить количество столбцов для этой конкретной строки с помощью "scores[row].length".

Вы можете также использовать "scores[0].length", если все строки имеют одинаковое количество столбцов.

Внутри цикла, оценки извлекаются с помощью двух индексов вместо только одного, как в 1D случае.

Первый индекс это строка, которая задается в качестве параметра, и 2-й индекс «с» это индекс цикла, который проходит данную строку.

После выхода из цикла, среднее строки вычисляется путем деления итоговой суммы, используя количество столбцов, а затем возвращается в качестве результата для метода aveByRow.

Среднее столбца может быть вычислено таким же образом, за исключением того, что параметр, передаваемый методу "aveByCol" является индексом столбца.

Число элементов, подлежащих включению в расчет среднего, это количество строк или scores.length.

Обратите внимание, чем отличается от предыдущего метода, мы просто используем scores здесь без индекса.

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

Среднее столбца затем вычисляется и возвращается.

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

Это метод maxIndex, который мы обсуждали для нахождения местоположения максимального значения в 1D массиве.

Подобно нахождению среднего, при попытке найти максимум или минимум для 2D массива, Вы должны определить, является ли это для строки, столбца или всего массива.

Этот метод "maxRowIndex" для нахождения местоположения максимального элемента для заданного столбца, который приведен в качестве параметра.

В примере оценки теста, вы можете подумать об этом, как найти студента, который получил наибольшее количество баллов в данном тесте, и индекс строки, возвращаемый методом, это номер студенческого билета с наибольшим количеством баллов.

Так как мы будем вести цикл по строкам, максимальный размер будет scores.length.

Внутри цикла, единственная разница между этим и предыдущим методом, это то, что есть два индекса для scores.

Для прохождения строки, второй индекс или индекс столбца является фиксированным, и первый индекс будет изменяться как индекс цикла.

mIndex будет возвращен как результат в конце.

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

Demo работы с 2D массивами

Давайте сделаем краткую демонстрацию на классе Scores, который мы только что обсуждали.

Проект уже открыт в BlueJ.

Вот метод "initializeAllScores", метод "getScoresByIndices" и метод "printAllScores".

Так это должно быть таким же, как то, что мы только что обсуждали.

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

Это было бы то же самое, как вызов метода "initializeAllScores".

Скомпилируйте программу, и нет ошибки.

Если вы запустите программу путем создания экземпляра, вызывая метод "printAllScores", вывод отображается здесь.

Значения, которые печатаются здесь, должны быть такими же, как начальные значения, определенные в массиве.

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

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

Таким образом, вместо инициализации двумерного массива с одинаковым числом столбцов для каждой строки, давайте оставим первую строку ту же самую с 4 элементами, но уменьшим число элементов во второй строке на единицу.

Это означает, что здесь есть только 3 элемента, а затем в третьей строке на 2 элемента.

Последняя строка, или 4-я строка имеет только 1 элемент.

Так что этот массив, если он работает, будет иметь разные столбцы для разных строк.

Давайте посмотрим, позволит ли Java нам сделать это.

Давайте скомпилируем программу; и она компилируется без проблем.

Так что теперь давайте попробуем запустить программу. И экземпляр объекта создается.

Если вы запустите программу с помощью "printAllScores", будет ошибка.

Ошибка ArrayIndexOutOfBounds, и она появляется именно в том выражении, когда мы вызываем getScoreByIndices.

Мы получаем эту ошибку, потому что мы предполагаем, что все строки имеют одинаковое число столбцов, получаемое как scores[0].length.

Но на самом деле, массив имеет разное количество столбцов для разных строк.

Так как же нам это исправить?

Одним из способов исправить это, изменить numOfCols здесь, во внутреннем цикле для "printAllScores", вместо использования numOfCols для всех строк, мы можем изменить это на scores[r].length.

С этим изменением, внутренний цикл будет ограничивать количество доступа к элементам в разных строках по количеству столбцов в этой строке, которое дается как scores[r].length.

И это даст нам число столбцов, с учетом индексации r, который является индексом строки.

Давайте снова скомпилировать программу, и нет ошибки.

И давайте снова запустим программу путем создания экземпляра.

Если вы вызываете метод "printAllScores" снова, вы можете видеть, что результат печатается здесь и имеет различное число элементов для разных строк, что то же самое, как массив был инициализирован.