Перейти к основному содержимому

Массивы в Java: справочное руководство

· 9 мин. чтения

1. Введение

В этом руководстве мы подробно рассмотрим основную концепцию языка Java — массивы.

Сначала мы увидим, что такое массив, а затем как их использовать; в целом, мы рассмотрим, как:

  • Начните работу с массивами
  • Чтение и запись элементов массива
  • Цикл по массиву
  • Преобразование массивов в другие объекты, такие как список или потоки
  • Сортировка, поиск и объединение массивов

2. Что такое массив?

Прежде всего, нам нужно определить, что такое массив? Согласно документации Java , массив — это объект, содержащий фиксированное количество значений одного типа . Элементы массива индексируются, что означает, что мы можем получить к ним доступ с помощью чисел (называемых индексами ).

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

Существуют массивы примитивных типов и массивы объектных типов. Это означает, что мы можем использовать массивы int, float, boolean, …, а также массивы String, Object и пользовательских типов.

3. Настройка массива

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

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

Начнем с объявления и инициализации.

3.1. Декларация

Начнем с декларации. Есть два способа объявить массив в Java:

int[] anArray;

или же:

int anOtherArray[];

Первый используется более широко, чем второй .

3.2. Инициализация

Теперь пришло время узнать, как инициализировать массивы. Опять же, существует несколько способов инициализации массива. Здесь мы рассмотрим основные из них, но в этой статье инициализация массивов рассматривается подробно.

Начнем с простого:

int[] anArray = new int[10];

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

При использовании этого метода мы инициализируем каждый элемент его значением по умолчанию , здесь 0 . При инициализации массива Object элементы по умолчанию имеют значение null .

Теперь мы увидим другой способ, дающий нам возможность устанавливать значения в массив непосредственно при его создании:

int[] anArray = new int[] {1, 2, 3, 4, 5};

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

4. Доступ к элементам

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

Например, этот небольшой фрагмент кода выведет на консоль число 10:

anArray[0] = 10;
System.out.println(anArray[0]);

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

При доступе к ячейке, если переданный индекс отрицательный или выходит за пределы последней ячейки, Java выдаст исключение ArrayIndexOutOfBoundException .

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

5. Перебор массива

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

Первый способ — использовать цикл for :

int[] anArray = new int[] {1, 2, 3, 4, 5};
for (int i = 0; i < anArray.length; i++) {
System.out.println(anArray[i]);
}

Это должно вывести на консоль числа от 1 до 5. Как мы видим, мы использовали свойство length . Это общедоступное свойство, дающее нам размер массива.

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

int[] anArray = new int[] {1, 2, 3, 4, 5};
for (int element : anArray) {
System.out.println(element);
}

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

  • нам не нужно изменять массив (помещение другого значения в элемент не изменит элемент в массиве)
  • нам не нужны индексы, чтобы делать что-то еще

6. Варарги

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

void varargsMethod(String... varargs) {}

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

Здесь нам нужно знать, что внутри тела метода параметр varargs превращается в массив. Но мы также можем передать массив напрямую в качестве аргумента. Давайте посмотрим, как это сделать, повторно используя метод примера, объявленный выше:

String[] anArray = new String[] {"Milk", "Tomato", "Chips"};
varargsMethod(anArray);

Будет вести себя так же, как:

varargsMethod("Milk", "Tomato", "Chips");

7. Преобразование массива в список

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

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

int[] anArray = new int[] {1, 2, 3, 4, 5};

List<Integer> aList = new ArrayList<>();
for (int element : anArray) {
aList.add(element);
}

Но есть и другой способ, чуть более лаконичный:

Integer[] anArray = new Integer[] {1, 2, 3, 4, 5};
List<Integer> aList = Arrays.asList(anArray);

Статический метод Arrays.asList принимает аргумент типа varargs и создает список с переданными значениями. К сожалению, этот метод имеет некоторые недостатки:

  • Невозможно использовать массив примитивных типов
  • Мы не можем добавлять или удалять элементы из созданного списка, так как это вызовет исключение UnsupportedOperationException .

8. От массива к потоку

Теперь мы можем преобразовывать массивы в списки, но начиная с Java 8 у нас есть доступ к Stream API , и мы можем захотеть превратить наши массивы в Stream . Java предоставляет нам для этого метод Arrays.stream :

String[] anArray = new String[] {"Milk", "Tomato", "Chips"};
Stream<String> aStream = Arrays.stream(anArray);

При передаче массива объектов методу он возвращает поток соответствующего типа (например, Stream<Integer> для массива Integer ). При передаче примитива он вернет соответствующий примитив Stream .

Также возможно создать поток только на подмножестве массива:

Stream<String> anotherStream = Arrays.stream(anArray, 1, 3);

Это создаст Stream<String> только со строками «Tomato» и «Chips» (первый индекс является включающим, а второй — исключающим).

9. Сортировка массивов

Давайте теперь посмотрим, как отсортировать массив, то есть переставить его элементы в определенном порядке. Класс Arrays предоставляет нам метод сортировки . Подобно потоковому методу, sort имеет множество перегрузок.

Есть перегрузки для сортировки:

  • Массивы примитивного типа: отсортированные по возрастанию
  • `Массивы объектов (эти объекты должны реализовывать интерфейс Comparable ): которые сортируются в соответствии с естественным порядком (на основе метода compareTo из Comparable)`
  • Общие массивы: которые отсортированы в соответствии с заданным Компаратором

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

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

Давайте посмотрим, как все это работает, на нескольких примерах:

int[] anArray = new int[] {5, 2, 1, 4, 8};
Arrays.sort(anArray); // anArray is now {1, 2, 4, 5, 8}

Integer[] anotherArray = new Integer[] {5, 2, 1, 4, 8};
Arrays.sort(anotherArray); // anotherArray is now {1, 2, 4, 5, 8}

String[] yetAnotherArray = new String[] {"A", "E", "Z", "B", "C"};
Arrays.sort(yetAnotherArray, 1, 3,
Comparator.comparing(String::toString).reversed()); // yetAnotherArray is now {"A", "Z", "E", "B", "C"}

10. Поиск в массиве

Поиск в массиве довольно прост, мы можем перебирать массив и искать наш элемент среди элементов массива:

int[] anArray = new int[] {5, 2, 1, 4, 8};
for (int i = 0; i < anArray.length; i++) {
if (anArray[i] == 4) {
System.out.println("Found at index " + i);
break;
}
}

Здесь мы искали номер 4 и нашли его по индексу 3.

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

К счастью, Java предоставляет нам метод Arrays.binarySearch . Мы должны дать ему массив и элемент для поиска.

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

Давайте посмотрим на пример использования метода бинарного поиска:

int[] anArray = new int[] {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(anArray, 4);
System.out.println("Found at index " + index);

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

11. Объединение массивов

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

int[] anArray = new int[] {5, 2, 1, 4, 8};
int[] anotherArray = new int[] {10, 4, 9, 11, 2};

int[] resultArray = new int[anArray.length + anotherArray.length];
for (int i = 0; i < resultArray.length; i++) {
resultArray[i] = (i < anArray.length ? anArray[i] : anotherArray[i - anArray.length]);
}

Как мы видим, когда индекс все еще меньше длины первого массива, мы добавляем элементы из этого массива. Затем добавляем элементы из второго. Мы можем использовать метод Arrays.setAll , чтобы избежать написания цикла:

int[] anArray = new int[] {5, 2, 1, 4, 8};
int[] anotherArray = new int[] {10, 4, 9, 11, 2};

int[] resultArray = new int[anArray.length + anotherArray.length];
Arrays.setAll(resultArray, i -> (i < anArray.length ? anArray[i] : anotherArray[i - anArray.length]));

Этот метод установит все элементы массива в соответствии с заданной функцией. Эта функция связывает индекс с результатом.

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

System.arraycopy(anArray, 0, resultArray, 0, anArray.length);
System.arraycopy(anotherArray, 0, resultArray, anArray.length, anotherArray.length);

Как видим, мы копируем первый массив, затем второй (после последнего элемента первого).

12. Заключение

В этой подробной статье мы рассмотрели основные и некоторые дополнительные способы использования массивов в Java.

Мы видели, что Java предлагает множество методов для работы с массивами через служебный класс Arrays . Существуют также служебные классы для управления массивами в таких библиотеках, как Apache Commons или Guava .

Полный код этой статьи можно найти на нашем GitHub .