Appearance
Массивы
Массив - это структура данных, которая хранит фиксированное количество элементов одного типа. Представь, что тебе нужно сохранить 100 оценок студентов. Создавать 100 переменных int mark1, mark2, … - это безумие. Массив позволяет хранить все значения под одним именем и обращаться к ним по индексу.
Объявление и создание массива
Объявление
Объявление массива мало чем отличается от объявления переменной. Единственное отличие - наличие квадратных скобок.
Синтаксис:
java
<тип данных>[] <имя массива>;
int[] numbers;
String[] words;
// Также допустима (но нежелательна) запись вида:
int numbers[];
На этом этапе массив ещё не создан, памяти под элементы не выделено. Мы просто говорим, что в обозримом будущем тут появится массив.
C-style объявление массивов
В примере выше показано объявление массива int numbers[]. Так принято объявлять массивы в С/С++, но не в Java. Несмотря на то, что такая запись допустима, старайтесь её избегать. Например, IntelliJ IDEA даже предупредит вас, что эта запись не совсем корректна. Так что используйте первый вариант из примера выше.
Создание с помощью new
java
int[] numbers;
numbers = new int[10]; // создаём массив из 10 целых чисел
// Или, по аналогии с переменными, объявление и создание можно объединить:
String[] words = new String[10];
// Также размер можно передать в виде переменной:
int n = 15;
double[] digits = new double[n]; // массив размером в 15 элементов
Размер массива
Размер массива всегда указывается при его создании и является фиксированным. После создания массива его размер изменить нельзя!
После создания массива все элементы автоматически инициализируются значениями по умолчанию:
- числовые типы -
0; - для
boolean-false; - для ссылочных типов (например,
String) -null;
Свойство length
У каждого массива есть свойство length - это количество элементов.
java
int[] numbers = new int[10];
System.out.println(numbers.length); // 10
А почему после length не ставятся скобки?
До этого, бОльшая часть команд заканчивалась двумя круглыми скобками (например, scanner.nextInt()). Почему их нет после length? Забегая вперёд:
- Методы:
nextInt(),nextLine()и другие команды, которые заканчиваются скобками - это методы (по сути просто функции внутри класса). Вызов любого метода подразумевает круглые скобки. Обычно там указываются аргументы метода (Например, при создании сканнера, в скобках мы указываем аргументScanner(System.in)). Но, даже если метод не подразумевает аргументов, мы всё равно ставим скобки, просто внутри ничего не пишем. Скобки позволяют отличить вызов метода от обращения к полю. - Поля:
length- это поле (переменная внутри класса). Для обращения к полю мы скобки не ставим. Именно, чтобы отличить вызов метода от обращения к полю, методы вызываются с круглыми скобками. Всё это подробнее будет затронуто во втором блоке курса - ООП.
P.S. Пока что методы, функции, обращения к полям, да и в целом всё подряд я называю командами, чтобы не забивать вам голову. Просто учтите, что это сознательное упрощение материала, и называть всё это командами - некорректно.
Доступ к элементам и индексация
Индексация (нумерация элементов, если простыми словами) начинается с нуля. Последний элемент любого массива имеет индекс length - 1.
java
int[] numbers = new int[5];
number[0] = 10; // обращение к элементу массива с индексом 0 (т.е. первому элементу)
numbers[4] = 20; // последний элемент
System.out.println(numbers[0]);
Если же попытаться обратиться по индексу, выходящему за пределы массива, программа упадёт с ошибкой:
java
numbers[5] = 0; // ArrayIndexOutOfBoundsException
Базовые операции с массивами
Заполнение через цикл
Самый распространенный способ обойти массив - использовать цикл for:
java
int[] numbers = new int[10];
for (int i = 0; i < numbers.length; i++) {
numbers[i] = i * 2; // заполняем чётными числами
}
Вывод массива в консоль
В Java даже вывод массива в консоль по-человечески не работает. Если просто написать:
java
System.out.println(numbers);
то в выводе будет что-то вроде [I@77556fd - это хэш-код объекта, а не содержимое объекта.
Чтобы вывести элементы, нужно либо явно пройтись по ним в цикле:
java
// Вариант с классическим for
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + " ");
}
// вариант с for-each
for (int i : numbers) {
System.out.print(i + " "); // здесь i - не индекс элемента, а сам элемент!
}
Либо использовать Arrays.toString():
java
import java.util.Arrays;
System.out.println(Arrays.toString(numbers));
Надоело постоянно писать System.out.println?
В Java 25 появился пакет IO, который предоставляет сокращённую форму для операций ввода и вывода. Вместо System.out... можно использовать IO.println() и IO.print(). Также пакет предоставляет сокращённую форму для ввода. В прошлой лекции обсуждался Scanner. Его нужно импортировать, создать и только потом использовать. Вместо этого можно использовать IO.readln().
java
// Вместо:
System.out.println("Hello World!");
// Можно написать:
IO.println("Hello World!");
// А для чтения ввода:
String a = IO.readln();
// Или с подсказкой
String b = IO.readln("Введите что-нибудь:"); // Никаких лишних импортов и создания сканнеров!
Обратите внимание, что IO.readln возвращает строку. Поэтому полностью заменить Scanner не получится. Если вам нужно принять на ввод что-то, кроме строки, придётся использовать сканнер.
Способы инициализации
Явное перечисление элементов
Самый простой способ - заполнить массив значениями при создании:
java
int[] numbers = {1, 2, 3, 4, 5};
Но так можно делать только при объявлении переменной. Если массив уже объявлен, такой синтаксис не сработает:
java
int[] numbers;
numbers = {1, 2, 3, 4, 5}; // Ошибка компиляции!
Если массив уже объявлен, то синтаксис заполнения меняется:
java
numbers = new int[]{1, 2, 3, 4, 5};
Многомерные массивы
Их не будет. Они мне не нравятся.
Класс Arrays - служебные методы
Этот класс находится в пакете java.util.Arrays и содержит множество полезных методов для работы с массивами.
Arrays.toString()
Обсуждался выше, но продублирую. Преобразует массив в строку для удобного вывода:
java
int[] arr = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(arr)); // [3, 1, 4, 1, 5]
Arrays.sort()
Сортирует массив по возрастанию:
java
int[] arr = {5, 2, 8, 1, 9};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 5, 8, 9]
Алгоритм сортировки Arrays.sort
Для примитивов используется быстрая сортировка с двумя опорными точками (Dual-Pivot QuickSort), для объектов — TimSort.
Arrays.binarySearch()
Бинарный поиск элемента в массиве. Работает только на отсортированном массиве. Возвращает индекс элемента или отрицательное число, если элемент не найден:
java
int[] arr = {1, 2, 5, 8, 9};
int index = Arrays.binarySearch(arr, 5);
System.out.println(index); // 2
int notFound = Arrays.binarySearch(arr, 7);
System.out.println(notFound); // -4 (точка вставки: -index - 1)
Arrays.copyOf()
Копирует массив (Почему нельзя просто написать int[] copy = numbers?). Можно изменить размер:
java
int[] original = {1, 2, 3};
// указываем массив, который хотим копировать, и размер нового массива
int[] copy = Arrays.copyOf(original, 5);
// Если размер нового массива больше исходного, то недостающие элементы заменяются на 0
System.out.println(Arrays.toString(copy)); // [1, 2, 3, 0, 0]
Если новый размер меньше исходного, лишние элементы отбрасываются:
java
int[] truncated = Arrays.copyOf(original, 2);
System.out.println(Arrays.toString(truncated)); // [1, 2]
Arrays.fill()
Заполняет все элементы массива указанным значением:
java
int[] arr = new int[5];
Arrays.fill(arr, 42);
System.out.println(Arrays.toString(arr)); // [42, 42, 42, 42, 42]
Arrays.equals()
Сравнивает содержимое двух массивов:
java
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
int[] c = {1, 2, 4};
System.out.println(Arrays.equals(a, b)); // true
System.out.println(Arrays.equals(a, c)); // false
Почему нельзя сравнить массивы через a == b?
Если мы сравниваем примитивные типы данных через оператор ==, то всё работает замечательно. Потому что мы сравниваем значения. Но если мы сравниваем так же ссылочные типы данных (String, массивы, другие типы данных, которые пишутся с большой буквы), то мы сравниваем ссылку на объект в памяти, а не значение. Например:
java
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(a == b); // false
Несмотря на то, что значения в массивах одинаковые, сравнение a == b вернуло false. Потому что сравнили мы в итоге ссылки.
По этой же причине нельзя сравнивать строки через ==.
P.S. Ссылочные типы данных и особенности работы с ними мы будем проходить позже
Копирование массивов: в чём разница?
Дан массив. Мы хотим создать его копию. Первое, что приходит в голову:
java
int[] a = {1, 2, 3}; // данный массив
int[] copy = a; // копия
System.out.println(Arrays.toString(copy)); // [1, 2, 3]
И вроде копия готова. Верно? Не совсем. Мы скопировали не содержимое массива, а ссылку на массив. Что это значит:
java
a[0] = 100; // Меняем первый элемент исходного массива
System.out.println(Arrays.toString(copy)); // [100, 2, 3]. Копия также изменилась.
Здесь copy и a ссылаются на один и тот же объект в памяти. Изменения через b видны через a. Это не копия, а просто ещё одна ссылка.
Если мы хотим создать независимую копию массива, необходимо использовать clone():
java
int[] a = {1, 2, 3};
int[] b = a.clone();
b[0] = 100; // меняем копию
System.out.println(Arrays.toString(a)); // [1, 2, 3]. Исходный массив не изменился
System.out.println(Arrays.toString(b)); // [100, 2, 3]
Практическая работа №3
1. Сумма элементов массива
Напишите программу, которая создаёт массив из 10 целых чисел, заполняет его случайными числами от 0 до 100 и выводит сумму всех элементов. (Как получить случайное число описано в четвёртом задании второй практической работы)
2. Поиск второго максимума
Вводится массив (сначала пользователь указывает кол-во элементов, потом сами элементы). После заполнения массива найдите второй по величине элемент.
3. Уникальные элементы
Вводится массив (сначала количество элементов, потом сами элементы). Выведите те его элементы, которые встречаются в массиве только один раз. Элементы нужно выводить в том порядке, в котором они встречаются в списке
4. Развернуть массив
Напишите метод, который берёт массив целых чисел (массив заполняется либо случайными числами, как в первом задании, либо пользователем, как во втором) и возвращает новый массив, в котором элементы идут в обратном порядке. Не используйте встроенные методы копирования.
5. Перестановка соседних элементов
Вводится массив. Переставьте соседние элементы списка (A[0] c A[1], A[2] c A[3] и т.д.). Если элементов нечетное число, то последний элемент остается на своем месте.
6*. Транспонирование матрицы
Дана квадратная матрица (двумерный массив (массив массивов)) размером N×N (квадратная). Напишите программу, которая транспонирует матрицу (меняет строки и столбцы местами). Транспонирование нужно выполнить на месте (в том же массиве), не создавая новый.
Пример создания двумерного массива
java
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};