Как сделать массив с разными типами данных java

Обновлено: 05.07.2024

1. Введение

У Java есть Платформа Коллекций, предоставляющая библиотеку общих структур данных для использования в разработке программного обеспечения Java. В Collections Framework отсутствует одна структура данных — массив. Тем не менее, Java Collections Framework имеет типизированные структуры данных ArrayList.java и Vector.java. Обе структуры данных используют динамический массив одного измерения, который использует базовый массив java.lang.Object.

Java предоставляет встроенный массив, который является объектом, который включен в языковую спецификацию начиная с Java 1.0 с 1995 года. В качестве объекта встроенный массив объявляется и создается в Java-коде в виде частиц. Встроенный массив представляет собой контейнер объектов с фиксированным числом и длиной, возможно, многих измерений.

Тем не менее, безопасность типов во время компиляции с использованием дженериков не полностью реализована. Особенно со встроенным объектом массива.

2. Универсальные массивы

Проблема в том, что дженерики используются со встроенным объектом массива в Java. Рассмотрим следующий класс Java, TestArray1.java, который объявляет два универсальных массива вокруг одного параметра универсального типа E. Исходный код класса Java:

Массив (array) это объект, хранящий в себе фиксированное количество значений одного типа. Другими словами, массив — это нумерованный набор переменных. Переменная в массиве называется элементом массива, а ее позиция в массиве задается индексом. Если, к примеру, нужно хранить большой объем различных имен, то неудобно для каждого имени создавать отдельную переменную. В этом случае следует использовать массив данных.

Для наглядности приведена картинка с The Java Tutorial.

Массив данных

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

В Java массивы данных являются объектами. Это значит, что имя, которое даётся каждому массиву, лишь указывает на адрес какого-то фрагмента данных в памяти. Кроме адреса в этой переменной ничего не хранится. Индекс массива, фактически, указывает на то, насколько надо отступить от начального элемента массива в памяти, чтобы получить доступ к нужному элементу.

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

Объявление массива

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

Массив данных можно объявить и так :

Однако, это не приветствуется соглашением по оформлению кода в Java. Поскольку квадратные скобки обозначают то, что мы имеем дело с массивом данных, то логичнее, чтобы они находились рядом с обозначением типа.

В приведенных выше примерах было объявлено 2 массива с именами firstArray и secondArray. Оба массива содержат элементы типа int.

Подобным образом можно объявить массив любого типа. Пример объявления массива :

Тип массива задается как type[], где type это тип данных содержащихся в нем элементов. Скобки являются специальным обозначением того, что переменные содержатся в массиве. Имя массива может быль любым, однако, оно должно соответствовать правилам именования переменных.

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

Резервирование памяти и инициализация массива

Для объявленного массива данных firstArray необходимо зарезервировть память при помощи ключевого слова new. Пример резервирования памяти :

В примере создается массив из 15 элементов типа int и присвоение его переменной firstArray. Объявлять имя массива и резервировать для него память также можно в одной строке.

При создании массива с помощью ключевого слова new, все элементы массива автоматически инициализированы нулевыми значениями. Для того, чтобы присвоить элементам массива свои начальные значения, необходимо провести его инициализацию. Инициализацию можно проводить поэлементно :

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

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

Для создания и инициализации массива можно также использовать упрощенную запись. Она не содержит слово new, а в скобках перечисляются начальные значения массива. Примеры объявления массивов с инициализацией :

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

Длину любого созданного массива запоминать не обязательно, т.к. имеется свойство, которое его хранит. Обратиться к этому свойству можно дописав .length к имени массива. Например:

Это свойство нельзя изменять, его можно только читать. Используя это свойство можно писать программный код для обработки массива даже не зная его конкретного размера. Например, так можно вывести на экран элементы любого массива с именем ar2 :

Последний элемент массива всегда ar2[ar2.length - 1].

Если программа выйдет за пределы индекса массива, то она будет остановлена с ошибкой ArrayOutOfBoundsException.

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

Массив может состоять не только из элементов какого-то встроенного типа (int, double и пр.), но и, в том числе, из объектов какого-то существующего класса и даже из других массивов. Массив который в качестве своих элементов содержит другие массивы называется многомерным массивом.

Чаще всего используются двумерные массивы. Такие массивы можно легко представить в виде матрицы. Каждая строка которой является обычным одномерным массивом, а объединение всех строк — двумерным массивом в каждом элементе которого хранится ссылка на какую-то строку матрицы.

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

Соответственно, для того, чтобы обратиться к элементу n-мерного массива нужно указать n индексов.

При создании массива можно указать явно размер каждого его уровня :

Но можно указать только размер первого уровня:

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

В результате получим массив следующего вида:

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

При этом можно обратиться к элементу с индексом 4 во второй строке ddd2[1][4]. Но если мы обратимся к элементу ddd2[0][4] или ddd2[2][4] — произойдёт ошибка, поскольку таких элементов просто нет. При этом ошибка будет возникать во время исполнения программы, т. е. компилятор её не увидит.

Обычно всё же используются двумерные массивы с равным количеством элементов в каждой строке. Для обработки двумерных массивов используются два вложенных друг в друга цикла с разными счётчиками. Пример заполнения двумерного массива случайными числами от 0 до 9 и вывод его в консоль в виде матрицы:

Копирование массива arraycopy

Стандартная библиотека Java содержит статический метод System.arraycopy(), который копирует массивы значительнее быстрее, чем при ручном копировании в цикле for. В аргументах arraycopy() передается исходный массив, начальная позиция копирования в исходном массиве, приёмный массив, начальная позиция копирования в приёмном массиве и количество копируемых элементов. Любое нарушение границ массива приведет к исключению.

Разработчик Avi Yehuda написал программу, которая вычисляет время на копирование с помощью цикла for и с помощью метода arraycopy() на примере с миллионом элементов. Ручное копирование у него заняло 182 мс, с помощью метода arraycopy() - 12 мс. Разница очевидна невооруженным глазом.

Я новичок в Java и на время создал массив объектов на Java.

у меня есть класс A, например -

но это только создание указателей (ссылок) на A, а не на 4 объекта. Правильно ли это? Я вижу, что когда я пытаюсь получить доступ к функциям/переменным в созданных объектах, я получаю исключение нулевого указателя. Чтобы иметь возможность манипулировать / получать доступ к объектам, я должен был сделать это --

это правильно или я делаю что-то неправильно? Если это правильно, это действительно странно.

EDIT: я нахожу это странным, потому что в C++ вы просто говорите new a[4], и он создает четыре объекта.

создает 4 ссылки, похожие на это

теперь вы не могли сделать a1.someMethod () без выделения a1 как

аналогично, с массивом вам нужно сделать

перед его использованием.

это правильно. Вы также можете сделать :

этот синтаксис также может использоваться для создания и инициализации массива в любом месте, например в аргументе метода:

Да, он создает только ссылки, которые имеют значение по умолчанию null. Именно поэтому вы получаете исключение NullPointerException вам нужно создать объекты отдельно и назначить ссылку. Существует 3 шага для создания массивов в Java -

декларация – в этом шаге мы указываем тип данных и размер массива, который мы собираемся создать. Но помните, мы пока не упоминаем размеры измерений. Они остаются пустыми.

экземпляров – на этом шаге мы создаем массив или выделяем память для массива, используя ключевое слово new. Именно на этом шаге мы упоминаем размеры измерений массива.

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

Объявление Массивов В Java

так мы объявляем одномерный массив в Java –

Oracle рекомендует использовать прежний синтаксис для объявления массивов. Вот еще несколько примеров юридических деклараций -

и вот некоторые примеры незаконных деклараций -

экземпляров

вот как мы "создаем экземпляр" или выделяем память для массива -

когда JVM встречает new ключевое слово, он понимает, что он должен выделить память на что-то. И указав int[5] , мы имеем в виду, что мы хотим массив int s, размера 5. Таким образом, JVM создает память и назначает ссылку на вновь выделенную память массиву, который является "ссылкой" типа int[]

инициализации

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

все в одном. - мы можем объявить, создать экземпляр и инициализировать наш массив за один раз. Вот синтаксис –

здесь мы не упоминаем размер, потому что JVM может видеть, что мы даем 5 значений.

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

Аннотация: Лекция посвящена описанию массивов в Java. Массивы издавна присутствуют в языках программирования, поскольку при выполнении многих задач приходится оперировать целым рядом однотипных значений. Массивы в Java – один из ссылочных типов, который, однако, имеет особенности при инициализации, создании и оперировании со своими значениями. Наибольшие различия проявляются при преобразовании таких типов. Также объясняется, почему многомерные массивы в Java можно (и зачастую более правильно) рассматривать как одномерные. Завершается классификация типов переменных и типов значений, которые они могут хранить. В заключение рассматривается механизм клонирования Java, позволяющий в любом классе описать возможность создания точных копий объектов, порожденных от него.

Массивы как тип данных в Java

В отличие от обычных переменных, которые хранят только одно значение , массивы (arrays) используются для хранения целого набора значений. Количество значений в массиве называется его длиной, сами значения – элементами массива. Значений может не быть вовсе, в этом случае массив считается пустым, а его длина равной нулю.

Элементы не имеют имен, доступ к ним осуществляется по номеру индекса. Если массив имеет длину n , отличную от нуля, то корректными значениями индекса являются числа от 0 до n-1 . Все значения имеют одинаковый тип и говорится, что массив основан на этом базовом типе. Массивы могут быть основаны как на примитивных типах (например, для хранения числовых значений 100 измерений), так и на ссылочных (например, если нужно хранить описание 100 автомобилей в гараже в виде экземпляров класса Car ).

Сразу оговоримся, что в Java массив символов char[] и класс String являются различными типами. Их значения могут легко конвертироваться друг в друга с помощью специальных методов, но все же они не относятся к идентичным типам.

Как уже говорилось, массивы в Java являются объектами (примитивных типов в Java всего восемь и их количество не меняется), их тип напрямую наследуется от класса Object , поэтому все элементы данного класса доступны у объектов-массивов.

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

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

Объявление массивов

В качестве примера рассмотрим объявление переменной типа "массив, основанный на примитивном типе int ":

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

Количество пар квадратных скобок указывает на размерность массива. Для многомерных массивов допускается смешанная запись:

Переменная a имеет тип "двумерный массив, основанный на int ". Аналогично объявляются массивы с базовым объектным типом:

Создание переменной типа массив еще не создает экземпляры этого массива. Такие переменные имеют объектный тип и хранят ссылки на объекты, однако изначально имеют значение null (если они являются полями класса; напомним, что локальные переменные необходимо явно инициализировать). Чтобы создать экземпляр массива, нужно воспользоваться ключевым словом new , после чего указывается тип массива и в квадратных скобках – длина массива.

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

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

Ошибка возникнет только на этапе выполнения программы.

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

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

Поскольку для экземпляра массива длина является постоянной характеристикой, для всех массивов существует специальное поле length , позволяющее узнать ее значение. Например:

Значение индекса массива всегда имеет тип int . При обращении к элементу можно также использовать byte , short или char , поскольку эти типы автоматически расширяются до int . Попытка задействовать long приведет к ошибке компиляции.

Соответственно, и поле length имеет тип int , а теоретическая максимально возможная длина массива равняется 2 31 -1 , то есть немногим больше 2 млрд.

Продолжая рассматривать тип массива, подчеркнем, что в качестве базового типа может использоваться любой тип Java, в том числе:

  • интерфейсы. В таком случае элементы массива могут иметь значение null или ссылаться на объекты любого класса, реализующего этот интерфейс;
  • абстрактные классы. В этом случае элементы массива могут иметь значение null или ссылаться на объекты любого неабстрактного класса-наследника.

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

Это дает интересную возможность для массивов, основанных на типе Object , хранить в качестве элемента ссылку на самого себя:

Инициализация массивов

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

Если создать массив на основе примитивного числового типа, то изначально после создания все элементы массива имеют значение по умолчанию, то есть 0 . Если массив объявлен на основе примитивного типа boolean , то и в этом случае все элементы будут иметь значение по умолчанию false . Выше рассматривался пример инициализации элементов с помощью цикла for .

Рассмотрим создание массива на основе ссылочного типа. Предположим, это будет класс Point . При создании экземпляра массива с применением ключевого слова new не создается ни один объект класса Point , создается лишь один объект массива. Каждый элемент массива будет иметь пустое значение null . В этом можно убедиться на простом примере:

Результатом будут лишь слова null .

Далее нужно инициализировать элементы массива по отдельности, например, в цикле. Вообще, создание массива длиной n можно рассматривать как заведение n переменных и работать с элементами массива (в последнем примере p[i] ) по правилам обычных переменных.

Кроме того, существует и другой способ создания массивов – инициализаторы. В этом случае ключевое слово new не используется, а ставятся фигурные скобки, и в них через запятую перечисляются значения всех элементов массива. Например, для числового массива явная инициализация записывается следующим образом:

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

Аналогично можно порождать массивы на основе объектных типов, например:

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

В конструкторе класса Child необходимо осуществить обращение к конструктору родителя и передать в качестве параметра ссылку на массив. Теоретически можно передать null , но это приведет в большинстве случаев к некорректной работе классов. Можно вставить выражение new String[2] , но тогда вместо значений firstName и lastName будут переданы пустые строки. Попытка записать приведет к ошибке компиляции, так можно только инициализировать переменные.

Корректное выражение выглядит так:

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

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

Теперь перейдем к рассмотрению многомерных массивов. Так, в следующем примере

переменная i ссылается на двумерный массив, который можно представить себе в виде таблицы 3х5 . Суммарно в таком массиве содержится 15 элементов, к которым можно обращаться через комбинацию индексов от (0, 0) до (2, 4) . Пример заполнения двумерного массива через цикл:

Результатом выполнения программы будет:

Однако такой взгляд на двумерные и многомерные массивы является неполным. Более точный подход заключается в том, что в Java нет двумерных, и вообще многомерных массивов, а есть массивы, базовыми типами которых являются также массивы. Например, тип int[] означает "массив чисел", а int[][] означает " массив массивов чисел". Поясним такую точку зрения.

Если создать двумерный массив и определить переменную x , которая на него ссылается, то, используя x и два числа в паре квадратных скобок каждое (например, x[0][0] ), можно обратиться к любому элементу двумерного массива. Но в то же время, используя x и одно число в паре квадратных скобок, можно обратиться к одномерному массиву, который является элементом двумерного массива. Его можно проинициализировать новым массивом с некоторой другой длиной и таблица перестанет быть прямоугольной – она примет произвольную форму. В частности, можно одному из одномерных массивов присвоить даже значение null .

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

Полезно подсчитать, сколько объектов порождается выражением new int[3][5] . Правильный подсчет таков: создается один массив массивов (один объект) и три массива чисел, каждый длиной 5 (три объекта). Итого, четыре объекта.

В рассмотренном примере три из них (массивы чисел) были тут же переопределены новыми значениями. Для таких случаев полезно использовать упрощенную форму выражения создания массивов:

Такая запись порождает один объект – массив массивов – и заполняет его значениями null . Теперь понятно, что и в этом, и в предыдущем варианте выражение x.length возвращает значение 3 – длину массива массивов. Далее можно с помощью выражений x[i].length узнать длину каждого вложенного массива чисел, при условии, что i неотрицательно и меньше x.length , а также x[i] не равно null . Иначе будут возникать ошибки во время выполнения программы.

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

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

В этом примере порождается четыре объекта. Это, во-первых, массив массивов длиной 4, а во-вторых, три массива чисел с длинами 2, 1, 0, соответственно.

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

Класс массива

Поскольку массив является объектным типом данных , можно попытаться представить себе, как выглядело бы объявление класса такого типа. На самом деле эти объявления не хранятся в файлах, или еще каком-нибудь формате. Учитывая, что массив может быть объявлен на основе любого типа и иметь произвольную размерность, это физически невыполнимо, да и не требуется. Вместо этого во время выполнения приложения виртуальная машина генерирует эти объявления динамически на основе базового типа и размерности, а затем они хранятся в памяти в виде таких же экземпляров класса Class , как и для любых других типов.

Рассмотрим гипотетическое объявление класса для массива, основанного на неком объектном типе Element .

Объявление класса начинается с перечисления модификаторов, среди которых особую роль играют модификаторы доступа. Класс массива будет иметь такой же уровень доступа, как и базовый тип. То есть если Element объявлен как public -класс, то и массив будет иметь уровень доступа public . Для любого примитивного типа класс массива будет public . Можно также указать модификатор final , поскольку никакой класс не может наследоваться от класса массива.

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

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

Тело класса содержит объявление одного public final поля length типа int . Кроме того, переопределен метод clone() для поддержки интерфейса Cloneable .

Сведем все вышесказанное в формальную запись класса:

Таким образом, экземпляр типа массив является полноценным объектом, который, в частности, наследует все методы, определенные в классе Object , например, toString() , hashCode() и остальные.

Читайте также: