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 из - Общие массивы: которые отсортированы в соответствии с заданным
Компаратором
Кроме того, можно сортировать только определенную часть массива (передавая методу начальный и конечный индексы).
Алгоритмы, лежащие в основе метода сортировки , — это
быстрая сортировка
и сортировка слиянием
для примитивных и других массивов соответственно.
Давайте посмотрим, как все это работает, на нескольких примерах:
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 .