Массив - это список фиксированного размера
Массив в Java хранит фиксированное количество значений одного типа, расположенных по порядку и доступных по позиции. «Фиксированный» - ключевое слово: как только вы создаёте массив, его длина уже никогда не меняется. И «одного типа»: int[] хранит только значения int, String[] хранит только строки. Эта жёсткость - плата за то, что массивы быстры и компактны в памяти.
Тип массива объявляется добавлением [] после типа элемента, после чего вы либо присваиваете литерал, либо выделяете память с помощью new:
Скобки могут стоять после типа (int[] scores) или после имени (int scores[]). Оба варианта компилируются, но int[] scores - идиоматический стиль Java: ставьте [] рядом с типом, где им и место.
Создание массива с помощью new
Когда вы ещё не знаете значений, но знаете размер, используйте new. Это выделяет ячейки и заполняет их значением по умолчанию для типа:
Значения по умолчанию - не случайный мусор: Java инициализирует каждый элемент нулём. Числовые типы начинаются с 0 (или 0.0), boolean начинается с false, а ссылочные типы вроде String начинаются с null. Этот null стоит запомнить: только что выделенный String[] полон null-ов, и вызов метода на одном из них выбрасывает NullPointerException.
Индексация начинается с нуля
Вы читаете и записываете элементы по индексу, отсчитывая от 0. Последний допустимый индекс всегда равен length - 1:
В отличие от массивов в некоторых скриптовых языках, выход за границы не возвращает тихо null или undefined - он выбрасывает исключение. Попробуйте прочитать несуществующий индекс:
Эта программа падает с ошибкой:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
Это одна из самых частых ошибок новичков. Исправление почти всегда сводится к ошибке на единицу (off-by-one) в условии цикла - использование <= там, где имелось в виду <.
length - это поле, а не метод
Каждый массив хранит свой размер в поле length. Внимание: без круглых скобок.
Вот подвох, на который хоть раз попадается каждый: массивы используют .length (поле), а String использует .length() (метод). Написать nums.length() для массива - ошибка компиляции, как и написать text.length для строки. Одно и то же слово, разные правила - у массива оно без скобок.
Перебор массива
Цикл for-each вы изучили на предыдущей странице, и это самый чистый способ прочитать каждый элемент, когда индекс вам не нужен:
Когда вам нужен индекс - чтобы вывести номера позиций, изменить элементы на месте или пройти в обратном порядке - возьмите классический индексный цикл for с length в качестве границы:
Условие - i < names.length, а не <=. Использование <= делает один лишний шаг и выбрасывает исключение выхода за границы из предыдущего раздела. Простое правило: for-each, когда вы только читаете значения, индексный for, когда вам нужна позиция или вы хотите записывать обратно в массив.
Многомерные массивы
Двумерный массив на самом деле является массивом массивов - представьте его как сетку из строк и столбцов. Вы добавляете вторую пару скобок:
grid[строка][столбец] выбирает одну ячейку. Поскольку каждая строка - это отдельный массив, grid.length - это количество строк, а grid[0].length - количество столбцов в первой строке. Строки даже не обязаны быть одинаковой длины (такое называют зубчатым массивом), хотя большинство сеток прямоугольные.
Класс-утилита Arrays
У самих массивов почти нет методов - их нельзя напрямую отсортировать или с пользой вывести. Для этого Java предоставляет класс-утилиту java.util.Arrays, полный статических помощников:
Несколько моментов, на которые стоит опереться. Arrays.toString(arr) - правильный способ вывести массив: при прямом выводе вы получите бесполезную строку вроде [I@1b6d3586. Arrays.sort сортирует на месте. Arrays.copyOf - это способ «изменить размер»: он возвращает новый массив запрошенной длины, дополняя значениями по умолчанию (две лишние ячейки выше равны 0). А Arrays.binarySearch работает корректно только на уже отсортированном массиве.
Массивы против ArrayList
Используйте обычный массив, когда размер известен заранее и не будет меняться - фиксированный набор дней недели, игровое поле, данные пикселей. Массивы быстры и легковесны, но их фиксированная длина - реальное ограничение: вы не можете добавить седьмой элемент в массив длины 6, не построив совершенно новый.
Как только вам нужно свободно добавлять и удалять элементы, массив становится неудобным. Для этого и существует ArrayList - изменяемый, растущий список, который берёт копирование на себя.
Далее: ArrayList
Когда вы не знаете, сколько элементов у вас будет, или это число меняется по ходу работы программы, массив фиксированной длины мешает. ArrayList - ответ Java: список, который растёт и сжимается по требованию, с методами add и remove вместо жонглирования индексами. Об этом - дальше.
Часто задаваемые вопросы
Как объявить и инициализировать массив в Java?
Объявите тип со скобками и присвойте литерал: int[] nums = {1, 2, 3};. Чтобы создать пустой массив фиксированного размера, используйте new: int[] nums = new int[5]; - это даёт 5 ячеек, каждая заполнена значением по умолчанию (0 для int, null для объектов).
Как узнать длину массива в Java?
Используйте поле length (без круглых скобок): nums.length. Это поле, а не метод, поэтому nums.length() - это ошибка компиляции. Обратите внимание: String использует .length() со скобками, а массивы используют .length без них - классическая путаница.
Может ли массив в Java изменить размер после создания?
Нет. Массив в Java имеет фиксированную длину, заданную при создании, и его нельзя ни увеличить, ни уменьшить. Если вам нужен список с изменяемым размером, используйте ArrayList. Чтобы "изменить размер" массива, пришлось бы создать новый, больший, и скопировать в него элементы.
Что такое ArrayIndexOutOfBoundsException в Java?
Это ошибка времени выполнения, которую вы получаете при обращении к несуществующему индексу - меньше 0 или >= array.length. В отличие от некоторых языков, Java не возвращает значение по умолчанию или null при выходе за границы; она выбрасывает исключение. Допустимые индексы всегда идут от 0 до length - 1.