Как перевернуть (развернуть) массив в Java
Перевернуть массив (реверс, разворот или инвертирование массива) — значит расположить его элементы в обратном порядке: первый становится последним, а последний — первым. Из массива {1, 2, 3, 4} нужно получить {4, 3, 2, 1}. В этом уроке разберём, как сделать это на месте (без лишней памяти), как развернуть массив, не изменяя оригинал, и какие готовые средства есть в стандартной библиотеке Java.
Реверс массива на месте
Самый эффективный способ развернуть массив — поменять местами симметричные элементы, ничего не выделяя дополнительно. Находим середину массива — array.length / 2. Перебираем элементы от начала до середины и одновременно меняем местами элементы с индексом i и array.length - i - 1. Для обмена значениями используем временную переменную:
public class ArrayInverter {
public static void invert(int[] array) {
for (int i = 0; i < array.length / 2; i++) {
int tmp = array[i];
array[i] = array[array.length - i - 1];
array[array.length - i - 1] = tmp;
}
}
} Цикл проходит только половину массива: элементы за серединой уже переставлены на первых итерациях. Если длина нечётная, центральный элемент остаётся на месте — его менять не нужно. Такой алгоритм работает за время O(n) и не требует дополнительной памяти (O(1)).
Обратите внимание: в классе ArrayInverter нет метода main() — метод invert() будем вызывать из другого класса. Для вызова метода из другого класса пишем имя класса перед его именем — ArrayInverter.invert(array1).
Полезно знать
В стандартной библиотеке Java нет готового метода Arrays.reverse() для массивов — в отличие от Collections.reverse() для списков. Поэтому реверс массива примитивов чаще всего пишут вручную циклом, как показано выше, либо берут ArrayUtils.reverse() из сторонней библиотеки Apache Commons Lang.
Проверяем метод на разных массивах
Проверим invert() на граничных случаях: пустой массив, один элемент, чётная и нечётная длина. Для вывода массива удобно использовать Arrays.toString():
import java.util.Arrays;
public class ArrayInverterExample1 {
public static void main(String[] args) {
int[] array1 = new int[]{};
System.out.print(Arrays.toString(array1) + " => ");
ArrayInverter.invert(array1);
System.out.println(Arrays.toString(array1));
array1 = new int[]{0};
System.out.print(Arrays.toString(array1) + " => ");
ArrayInverter.invert(array1);
System.out.println(Arrays.toString(array1));
array1 = new int[]{0, 1};
System.out.print(Arrays.toString(array1) + " => ");
ArrayInverter.invert(array1);
System.out.println(Arrays.toString(array1));
array1 = new int[]{0, 1, 2};
System.out.print(Arrays.toString(array1) + " => ");
ArrayInverter.invert(array1);
System.out.println(Arrays.toString(array1));
array1 = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.print(Arrays.toString(array1) + " => ");
ArrayInverter.invert(array1);
System.out.println(Arrays.toString(array1));
}
} Результат выполнения:
[] => []
[0] => [0]
[0, 1] => [1, 0]
[0, 1, 2] => [2, 1, 0]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] => [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] Метод корректно обрабатывает и пустой массив, и массив из одного элемента: в обоих случаях array.length / 2 равно нулю, поэтому цикл не выполняется ни разу.
Убираем дублирование кода
Если присмотреться к ArrayInverterExample1, видно, что один и тот же блок кода повторяется пять раз. Дублирование — плохая практика: любую правку пришлось бы вносить в пяти местах. Вынесем повторяющийся код в отдельный метод testInvert() и будем вызывать его с разными массивами:
import java.util.Arrays;
public class ArrayReverseExample {
public static void main(String[] args) {
testInvert(new int[]{});
testInvert(new int[]{0});
testInvert(new int[]{0, 1});
testInvert(new int[]{0, 1, 2});
testInvert(new int[]{0, 1, 2, 3, 4});
}
private static void testInvert(int[] array) {
System.out.print(Arrays.toString(array) + " => ");
ArrayInverter.invert(array);
System.out.println(Arrays.toString(array));
}
} Код стал короче и понятнее, а логику вывода теперь легко изменить в одном месте.
Разворот без изменения оригинала
Метод invert() меняет исходный массив — это называется изменением «на месте». Иногда оригинал нужно сохранить, а перевёрнутую версию получить отдельно. Тогда создаём новый массив и заполняем его в обратном порядке:
public static int[] reversedCopy(int[] source) {
int[] result = new int[source.length];
for (int i = 0; i < source.length; i++) {
result[i] = source[source.length - 1 - i];
}
return result;
} Здесь исходный массив source остаётся нетронутым, а результат возвращается новым массивом. Такой подход требует дополнительной памяти O(n), зато не портит входные данные.
Разворот через Stream
Для массива int[] можно развернуть элементы декларативно с помощью IntStream, перебирая индексы в обратном порядке:
import java.util.Arrays;
import java.util.stream.IntStream;
public class ArrayStreamReverse {
public static void main(String[] args) {
int[] source = {1, 2, 3, 4, 5};
int[] reversed = IntStream.rangeClosed(1, source.length)
.map(i -> source[source.length - i])
.toArray();
System.out.println(Arrays.toString(reversed)); // [5, 4, 3, 2, 1]
}
} Вариант со Stream читается компактнее, но для примитивов уступает обычному циклу по скорости и всегда создаёт новый массив. На собеседовании чаще ждут именно ручной реверс на месте.
Collections.reverse для списка
Если данные хранятся не в массиве, а в List, разворачивать вручную не нужно — есть готовый метод Collections.reverse(), который меняет список на месте:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ListReverse {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
Collections.reverse(numbers);
System.out.println(numbers); // [4, 3, 2, 1]
}
} Важно: Collections.reverse() работает только со списками, а не с массивами. Массив примитивов int[] нельзя напрямую превратить в List<Integer> через Arrays.asList() — об этом ниже, в разделе о частых ошибках.
Массивы строк и объектов
Тот же алгоритм обмена подходит для массива любых объектов — нужно лишь заменить тип. Сделаем метод обобщённым, чтобы он разворачивал массив String[], Integer[] и любой другой ссылочный тип:
public static <T> void invert(T[] array) {
for (int i = 0; i < array.length / 2; i++) {
T tmp = array[i];
array[i] = array[array.length - i - 1];
array[array.length - i - 1] = tmp;
}
} Дженерики не работают с примитивами, поэтому для int[] оставляем отдельную версию с int, а обобщённый метод используем для массивов объектов. Например, вызов invert(new String[]{"a", "b", "c"}) превратит массив в {"c", "b", "a"}.
На чём чаще всего ошибаются
- Цикл до конца массива вместо середины: если перебирать все элементы (
i < array.length), массив развернётся, а затем развернётся обратно и вернётся в исходный вид. Границей должна быть именноarray.length / 2. - Забытый средний элемент. При нечётной длине центральный элемент трогать не нужно — он и так остаётся на своём месте. Цикл до середины обрабатывает это автоматически.
- Collections.reverse на массиве: метод принимает
List, а не массив, — наint[]он не скомпилируется. - Arrays.asList с массивом примитивов:
Arrays.asList(intArray)вернётListиз одного элемента — самого массива, а не список чисел. Для этого нужен массив-обёрткаInteger[]или потоки.
Часто задаваемые вопросы
Как перевернуть массив в Java без создания нового?
Меняйте местами симметричные элементы в цикле от начала до середины: элемент с индексом i и элемент с индексом array.length - i - 1, используя временную переменную. Это реверс «на месте» — за время O(n) и без дополнительной памяти.
Есть ли в Java готовый метод для разворота массива?
Нет, встроенного Arrays.reverse() в стандартной библиотеке не существует. Для списков есть Collections.reverse(), а для массивов можно взять ArrayUtils.reverse() из Apache Commons Lang или написать реверс вручную циклом.
Как развернуть массив, не изменяя оригинал?
Создайте новый массив той же длины и заполните его, идя по исходному массиву с конца: result[i] = source[source.length - 1 - i]. Исходный массив останется без изменений, а перевёрнутая копия вернётся отдельно.
Как перевернуть массив строк или объектов?
Тем же алгоритмом обмена, но с обобщённым методом: public static <T> void invert(T[] array). Он развернёт String[], Integer[] и любой ссылочный тип. Для int[] дженерики не подходят, поэтому для примитивов нужна отдельная версия.
Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии