Метод swap в Java: обмен значениями двух переменных
swap (от англ. «менять местами») — это обмен значениями между двумя переменными: то, что лежало в первой, оказывается во второй, и наоборот. Такая операция постоянно встречается при сортировке, перестановке элементов и решении алгоритмических задач. Разберём три способа выполнить обмен в Java и отдельно — почему «метод swap» для обычных переменных в Java не работает так, как ожидают новички.
Обмен через временную переменную
Самый простой и надёжный способ. Вводим временную переменную tmp, которая сохранит значение из первой переменной, пока мы перезаписываем её значением из второй:
int tmp = a;
a = b;
b = tmp; Полный пример:
public class SwapExample1 {
public static void main(String[] args) {
int a = 3;
int b = 5;
int tmp = a;
a = b;
b = tmp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
} Результат выполнения:
a = 5
b = 3 Этот вариант работает с любыми типами — int, double, String, ссылками на объекты — и не имеет подводных камней. Именно его стоит использовать по умолчанию.
Обмен без временной переменной (арифметика)
Иногда просят обменять значения без третьей переменной. Один из способов — сложение и вычитание:
a = a + b;
b = a - b;
a = a - b; Полный пример с пошаговым разбором в комментариях:
public class SwapExample2 {
public static void main(String[] args) {
int a = 3;
int b = 5;
a = a + b; // a = 8, b = 5
b = a - b; // a = 8, b = 3
a = a - b; // a = 5, b = 3
System.out.println("a = " + a);
System.out.println("b = " + b);
}
} Осторожно: переполнение
Сумма a + b может выйти за границы диапазона int (от минус 2 147 483 648 до 2 147 483 647). При переполнении промежуточный результат обрежется, и хотя итог обмена всё равно окажется верным за счёт того же переполнения при вычитании, полагаться на это в реальном коде не стоит. Для double и float арифметический трюк вообще ненадёжен из-за потери точности. Поэтому в продакшене используйте обмен через временную переменную.
Обмен через XOR
Ещё один трюк без временной переменной — побитовое исключающее ИЛИ (^). Он работает только для целочисленных типов, но не имеет проблемы переполнения:
a = a ^ b;
b = a ^ b;
a = a ^ b; Полный пример:
public class SwapExample3 {
public static void main(String[] args) {
int a = 3;
int b = 5;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println("a = " + a); // a = 5
System.out.println("b = " + b); // b = 3
}
} Работает это благодаря свойствам XOR: x ^ x == 0 и x ^ 0 == x. Важный нюанс: если случайно применить такой обмен к одной и той же переменной (например, arr[i] и arr[j] при i == j), она обнулится. XOR-обмен красиво смотрится на собеседовании, но в обычном коде проигрывает по читаемости варианту с временной переменной.
Почему метод swap не меняет переменные
Логично вынести обмен в отдельный метод swap. Но с примитивами это не сработает — Java всегда передаёт аргументы по значению (pass by value): метод получает копии, а не сами переменные.
public class SwapMethod {
// НЕ работает: x и y — копии, оригиналы a и b не изменятся
static void swap(int x, int y) {
int tmp = x;
x = y;
y = tmp;
}
public static void main(String[] args) {
int a = 3;
int b = 5;
swap(a, b);
System.out.println("a = " + a); // a = 3
System.out.println("b = " + b); // b = 5 — обмена не произошло
}
} Это одна из самых частых ловушек на собеседованиях. Написать универсальный метод swap(int, int) для двух локальных переменных в Java невозможно в принципе. Обойти ограничение можно, если передать не сами значения, а контейнер, ссылку на который можно изменить изнутри: массив, список или объект-обёртку.
Обмен элементов массива и списка
Если значения лежат в массиве, метод swap отлично работает: метод получает копию ссылки на массив, но это ссылка на тот же самый объект, поэтому изменения элементов видны снаружи.
static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
} Для коллекций в стандартной библиотеке уже есть готовый метод Collections.swap, который меняет местами два элемента списка по индексам:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SwapList {
public static void main(String[] args) {
List<String> list = new ArrayList<>(List.of("A", "B", "C"));
Collections.swap(list, 0, 2);
System.out.println(list); // [C, B, A]
}
} Что выбрать на практике
| Способ | Типы | Плюсы | Минусы |
|---|---|---|---|
| Временная переменная | Любые | Просто, надёжно, читаемо | Нужна лишняя переменная (незначимо) |
| Сложение и вычитание | Целые числа | Без третьей переменной | Риск переполнения, хуже читаемость |
XOR (^) | Целые числа | Без переполнения, без третьей переменной | Ломается при обмене элемента с самим собой |
Collections.swap | Элементы List | Готовый метод из JDK | Только для списков |
Вывод простой: в реальном коде почти всегда используйте обмен через временную переменную — он понятен, работает с любыми типами и не имеет скрытых ограничений. Приёмы с арифметикой и XOR полезно знать для собеседований, но в промышленном коде они лишь усложняют чтение. А если нужно обменять значения «в вызывающем коде», помните про передачу по значению: работайте через массив, список или объект-обёртку.
Часто задаваемые вопросы
Как написать метод swap для двух переменных в Java?
Для двух обычных (примитивных) переменных — никак: Java передаёт аргументы по значению, метод получает копии, и оригиналы не меняются. Обмен работает, только если передать контейнер — массив, List или объект-обёртку — и менять его содержимое внутри метода. Для локальных переменных выполняйте обмен напрямую, без метода.
Как поменять значения двух переменных без третьей переменной?
Двумя приёмами. Через арифметику: a = a + b; b = a - b; a = a - b; — но есть риск переполнения. Через XOR: a = a ^ b; b = a ^ b; a = a ^ b; — без переполнения, но только для целых чисел. В реальном коде удобнее и безопаснее обмен через временную переменную.
Безопасен ли обмен через сложение и вычитание?
Для целых чисел итог всегда верный, потому что переполнение при сложении компенсируется при вычитании. Но полагаться на переполнение в промышленном коде не стоит, а для double и float способ ненадёжен из-за потери точности. Предпочтительнее обмен через временную переменную.
Как обменять два элемента массива или списка?
Для массива напишите метод, принимающий массив и два индекса, и меняйте элементы через временную переменную — изменения видны снаружи, потому что метод работает с тем же объектом. Для списка используйте готовый метод Collections.swap(list, i, j).
Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии