Глава 10. Массивы
Оглавление
В языке программирования Java массивы являются объектами (§4.3.1), создаются динамически и могут быть присвоены переменным типа Object (§4.3.2). Все методы класса Object могут быть вызваны для массива.
Объект массива содержит ряд переменных. Количество переменных может быть нулевым, в этом случае массив считается пустым. Переменные, содержащиеся в массиве, не имеют имен; вместо этого они ссылаются с помощью выражений доступа к массиву, которые используют целочисленные индексы без знака, начиная с нуля. Эти переменные называются компонентами массива. Если массив имеет n компонентов, то n называется длиной массива; к компонентам массива обращаются с использованием целочисленных индексов от 0 до n - 1 включительно.
Все компоненты массива имеют один и тот же тип, называемый типом компонента массива. Если тип компонента массива — T, то тип самого массива записывается как T[].
Значение компонента массива типа float всегда является элементом набора значений с плавающей точкой (§4.2.3); аналогично, значение компонента массива типа double всегда является элементом набора значений с двойной точностью. Не допускается, чтобы значение компонента массива типа float было элементом набора значений с расширенной экспонентой с плавающей точкой, которое не является также элементом набора значений с плавающей точкой, и аналогично для значения компонента массива типа double.
Тип компонента массива сам может быть типом массива. Компоненты такого массива могут содержать ссылки на подмассивы. Если, начиная с любого типа массива, рассматривать тип его компонента, а затем (если это также тип массива) тип компонента этого типа и так далее, то в конечном итоге нужно будет достичь типа компонента, который не является типом массива; это называется элементным типом исходного массива, а компоненты на этом уровне структуры данных называются элементами исходного массива.
В некоторых ситуациях элементом массива может быть массив: если элементный тип — Object или Cloneable или java.io.Serializable, то некоторые или все элементы могут быть массивами, так как любой объект массива может быть присвоен любой переменной этих типов.
Типы массивов используются в объявлениях и в выражениях приведения типов (§15.16).
Тип массива записывается как имя элементарного типа, за которым следует некоторое количество пар пустых квадратных скобок []. Количество пар скобок указывает глубину вложенности массива.
Длина массива не является частью его типа.
Элементный тип массива может быть любым типом, как примитивным, так и ссылочным. В частности:
-
Допускаются массивы с типом интерфейса в качестве элементного типа.
Элементом такого массива может быть нулевая ссылка или экземпляр любого типа, реализующего интерфейс.
-
Допускаются массивы со ссылочным классом
abstractв качестве элементного типа.Элементом такого массива может быть нулевая ссылка или экземпляр любого подкласса класса
abstract, который сам по себе не являетсяabstract.
Надтипы типа массива указаны в §4.10.3.
Непосредственным суперклассом типа массива является Object.
Каждый тип массива реализует интерфейсы Cloneable и java.io.Serializable.
Переменная типа массива хранит ссылку на объект. Объявление переменной типа массива не создает объект массива или не выделяет памяти для компонентов массива. Она создает только саму переменную, которая может содержать ссылку на массив.
Однако, часть инициализатора декларатора (§8.3, §9.3, §14.4.1) может создать массив, ссылка на который затем становится начальным значением переменной.
Пример 10.2-1. Объявления переменных массивов
int[] ai; // array of int
short[][] as; // array of array of short
short s, // scalar short
aas[][]; // array of array of short
Object[] ao, // array of Object
otherAo; // array of Object
Collection<?>[] ca; // array of Collection of unknown type
Приведенные выше объявления не создают объектов массивов. Следующие примеры объявления переменных массивов, которые создают объекты массивов:
Exception ae[] = new Exception[3];
Object aao[][] = new Exception[2][3];
int[] factorial = { 1, 1, 2, 6, 24, 120, 720, 5040 };
char ac[] = { 'n', 'o', 't', ' ', 'a', ' ',
'S', 't', 'r', 'i', 'n', 'g' };
String[] aas = { "array", "of", "String", };
[] может появиться в качестве части типа в начале объявления, или как часть декларатора для конкретной переменной, или и то, и другое.
Например:
byte[] rowvector, colvector, matrix[];
Это объявление эквивалентно:
byte rowvector[], colvector[], matrix[][];
В объявлении переменной (§8.3, §8.4.1, §9.3, §14.14, §14.20) за исключением переменной с изменяемым количеством аргументов, тип массива переменной обозначается типом массива, который появляется в начале объявления, за которым следуют любые пары скобок, следующие за идентификатором переменной в деклараторе.
Например, объявление локальной переменной:
int a, b[], c[][];
эквивалентно последовательности объявлений:
int a; int[] b; int[][] c;
Скобки разрешены в деклараторах как отсылка к традиции C и C++. Однако общие правила объявления переменных разрешают скобки как в типе, так и в деклараторах, так что локальное объявление переменной:
float[][] f[][], g[][][], h[]; // Yechh!
эквивалентно последовательности объявлений:
float[][][][] f; float[][][][][] g; float[][][] h;
Не рекомендуется использовать «смешанную запись» в объявлении переменной массива, где скобки появляются как в типе, так и в деклараторах.
После создания объекта массива его длина никогда не меняется. Чтобы переменная массива ссылалась на массив другой длины, необходимо присвоить переменной ссылку на другой массив.
Одна переменная типа массива может содержать ссылки на массивы различной длины, так как длина массива не является частью его типа.
Если переменная массива v имеет тип A[], где A — ссылочный тип, то v может содержать ссылку на экземпляр любого типа массива B[], при условии, что B может быть присвоено A (§5.2). Это может привести к исключению во время выполнения на позднее присваивании; см. §10.5 для обсуждения.
Массив создается с помощью выражения создания массива (§15.10) или инициализатора массива (§10.6).
Выражение создания массива указывает элементный тип, количество уровней вложенных массивов и длину массива по крайней мере для одного уровня вложенности. Длина массива доступна как final переменная экземпляра length.
Инициализатор массива создаёт массив и предоставляет начальные значения для всех его компонентов.
Элемент массива доступен с помощью выражения доступа к массиву (§15.13), которое состоит из выражения, значение которого — ссылка на массив, за которым следует выражение индексации, заключённое в [ и ], как в A[i].
Все массивы имеют 0-нулевую базу индексации. Массив длиной n может быть индексирован целыми числами от 0 до n-1.
Пример 10.4-1. Доступ к массиву
class Gauss {
public static void main(String[] args) {
int[] ia = new int[101];
for (int i = 0; i < ia.length; i++) ia[i] = i;
int sum = 0;
for (int e : ia) sum += e;
System.out.println(sum);
}
}
Эта программа выводит:
5050
Программа объявляет переменную ia, имеющую тип массив int, то есть int[]. Переменная ia инициализируется ссылкой на вновь созданный объект массива, созданный выражением создания массива (§15.10). Выражение создания массива указывает, что массив должен иметь 101 компонентов. Длина массива доступна с помощью поля length, как показано. Программа заполняет массив целыми числами от 0 до 100, суммирует эти целые числа и выводит результат.
Массивы должны быть индексированы int значениями; short, byte или char значения также могут использоваться в качестве индексов, поскольку они подвергаются унарному числовому преобразованию (§5.6.1) и становятся int значениями.
Попытка доступа к элементу массива с long индексом приводит к ошибке компиляции.
Все обращения к массивам проверяются во время выполнения; попытка использовать индекс, меньший нуля или равный или больший длины массива, приводит к выбрасыванию ArrayIndexOutOfBoundsException исключения.
Для массива, тип которого A[], где A — тип ссылки, присваивание элементу массива проверяется во время выполнения, чтобы убедиться, что присваиваемое значение совместимо для присваивания с элементом.
Если тип присваиваемого значения не совместим для присваивания (§5.2) с типом элемента, выбрасывается ArrayStoreException исключение.
Если тип элемента массива не был бы реифицируемым (§4.7), Java Virtual Machine не мог бы выполнить проверку записи, описанную в предыдущем абзаце. Вот почему выражение создания массива с нереифицируемым типом элемента запрещено (§15.10). Можно объявить переменную типа массив, тип элемента которого не реифицируем, но присваивание результата выражения создания массива переменной обязательно вызовет предупреждение о недопустимом преобразовании (§5.1.9).
Пример 10.5-1. ArrayStoreException
class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
public static void main(String[] args) {
ColoredPoint[] cpa = new ColoredPoint[10];
Point[] pa = cpa;
System.out.println(pa[1] == null);
try {
pa[0] = new Point();
} catch (ArrayStoreException e) {
System.out.println(e);
}
}
}
Эта программа выводит:
true java.lang.ArrayStoreException: Point
Переменная pa имеет тип Point[], а переменная cpa имеет в качестве своего значения ссылку на объект типа ColoredPoint[]. Объект типа ColoredPoint может быть присвоен объекту типа Point; поэтому значение cpa может быть присвоено pa.
Ссылка на этот массив pa, например, проверка, является ли pa[1] типом null, не приведет к ошибке во время выполнения. Это происходит потому, что элемент массива типа ColoredPoint[] является
ColoredPoint, и каждый ColoredPoint может выступать в качестве Point, так как Point является суперклассом ColoredPoint.
С другой стороны, присвоение массиву pa может привести к ошибке во время выполнения. Во время компиляции присвоение элементу pa проверяется, чтобы убедиться, что присваиваемое значение является Point. Но так как pa содержит ссылку на массив ColoredPoint, присвоение допустимо только в том случае, если тип присваиваемого значения во время выполнения является, точнее, ColoredPoint.
Java Virtual Machine проверяет такую ситуацию во время выполнения, чтобы убедиться, что присвоение допустимо; если нет, выбрасывается ArrayStoreException исключение.
Инициализатор массива может быть указан в объявлении (§8.3, §9.3, §14.4) или в качестве части выражения создания массива (§15.10), чтобы создать массив и предоставить некоторые начальные значения.
ИнициализаторМассива:
{ СписокПеременныхоп ,оп }
СписокПеременных:
ИнициализаторПеременной
СписокПеременных , ИнициализаторПеременной
Следующее повторяется из §8.3, чтобы сделать представление здесь более понятным:
ИнициализаторПеременной:
Выражение
ИнициализаторМассива
Инициализатор массива записывается в виде списка выражений, разделённых запятыми, заключённых в фигурные скобки { и }.
Запятая после последнего выражения в инициализаторе массива может быть пропущена и игнорируется.
Каждый инициализатор переменной должен быть совместим для присваивания (§5.2) с типом элемента массива, в противном случае произойдёт ошибка компиляции.
Если тип элемента массива, который инициализируется, не реифицируем (§4.7), это ошибка компиляции.
Длина создаваемого массива равна количеству инициализаторов переменных, непосредственно заключённых в фигурные скобки инициализатора массива. Выделяется память для нового массива такой длины. Если памяти недостаточно для выделения массива, выполнение инициализатора массива завершается внезапно, вызвав OutOfMemoryError исключение. В противном случае создаётся одномерный массив заданной длины, и каждый элемент массива инициализируется своим значением по умолчанию (§4.12.5).
Затем инициализаторы переменных, непосредственно заключённые в фигурные скобки инициализатора массива, выполняются слева направо в текстовом порядке, в котором они встречаются в исходном коде. Инициализатор переменной n задаёт значение n-го элемента массива. Если выполнение инициализатора переменной завершается внезапно, то выполнение инициализатора массива завершается по той же причине. Если все выражения инициализатора переменной завершаются нормально, инициализатор массива завершается нормально со значением только что инициализированного массива.
Если тип элемента — тип массива, то инициализатор переменной, определяющий элемент, сам может быть инициализатором массива; то есть, инициализаторы массивов могут быть вложенными. В этом случае выполнение вложенного инициализатора массива создаёт и инициализирует объект массива путём рекурсивного применения алгоритма выше и присваивает его элементу.
Пример 10.6-1. Инициализаторы массивов
class Test {
public static void main(String[] args) {
int ia[][] = { {1, 2}, null };
for (int[] ea : ia) {
for (int e: ea) {
System.out.println(e);
}
}
}
}
Эта программа выводит:
1 2
до того, как вызвать NullPointerException исключение при попытке индексировать второй элемент массива ia, который является null-ссылкой.
Членами типа массива являются следующие элементы:
-
Поле
publicfinallength, содержащее количество компонентов массива.lengthможет быть положительным или нулевым. -
Метод
publicclone, который переопределяет метод с таким же именем в классеObjectи не вызывает проверенные исключения. Тип возвращаемого значения методаcloneдля типа массива T[]— T[].Клон многомерного массива является неглубоким (shallow), то есть он создает только один новый массив. Подмассивы будут совместно использоваться.
-
Все члены, унаследованные от класса
Object; единственный методObject, который не унаследован, это его методclone.
Таким образом, массив имеет те же поля и методы, что и следующий класс:
class A<T> implements Cloneable, java.io.Serializable {
public final int length = X ;
public T[] clone() {
try {
return (T[])super.clone(); // unchecked warning
} catch (CloneNotSupportedException e) {
throw new InternalError(e.getMessage());
}
}
}
Обратите внимание, что приведение типов в примере выше сгенерирует предупреждение о невыверенном преобразовании (§5.1.9), если массивы были бы реализованы именно так.
См. §9.6.3.4 для другой ситуации, когда разница между public и не-public методами Object требует особого внимания.
Пример 10.7-1. Массивы могут быть скопированы
class Test1 {
public static void main(String[] args) {
int ia1[] = { 1, 2 };
int ia2[] = ia1.clone();
System.out.print((ia1 == ia2) + " ");
ia1[1]++;
System.out.println(ia2[1]);
}
}
Эта программа выведет:
false 2
показывающее, что компоненты массивов, на которые ссылаются ia1 и ia2, являются разными переменными.
Пример 10.7-2. Общие подмассивы после копирования
Тот факт, что подмассивы совместно используются при клонировании многомерного массива, показан в этой программе:
class Test2 {
public static void main(String[] args) throws Throwable {
int ia[][] = { {1,2}, null };
int ja[][] = ia.clone();
System.out.print((ia == ja) + " ");
System.out.println(ia[0] == ja[0] && ia[1] == ja[1]);
}
}
Эта программа выведет:
false true
показывающее, что массив int[], который ia[0], и массив int[], который ja[0], — это один и тот же массив.
Каждый массив имеет связанный с ним объект класса, общий для всех других массивов с тем же типом компонентов.
Пример 10.8-1. Объект класса массива
class Test {
public static void main(String[] args) {
int[] ia = new int[3];
System.out.println(ia.getClass());
System.out.println(ia.getClass().getSuperclass());
}
}
Эта программа выведет:
class [I class java.lang.Object
где строка "[I" — это сигнатура типа во время выполнения для объекта класса "массив с компонентами типа int".
Пример 10.8-2. Объекты класса массива совместно используются
class Test {
public static void main(String[] args) {
int[] ia = new int[3];
int[] ib = new int[6];
System.out.println(ia.getClass() == ib.getClass());
System.out.println("ia has length=" + ia.length);
}
}
Эта программа выведет:
true ia has length=3
Программа использует метод getClass, унаследованный от класса Object, и поле length. Результат сравнения объектов Class в первом println показывает, что все массивы, компоненты которых имеют тип int, являются экземплярами одного и того же типа массива, которым является int[].
В языке программирования Java, в отличие от C, массив символов не является строкой, и ни строка, ни массив символов не завершаются '\u0000' (символом NUL).
Объект строки является неизменяемым, то есть его содержимое никогда не изменяется, в то время как массив символов имеет изменяемые элементы.
Метод toCharArray в классе String возвращает массив символов, содержащий ту же последовательность символов, что и строка. Класс StringBuffer реализует полезные методы для изменяемых массивов символов.
© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.