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

Поменять местами две переменные в Java

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

1. Обзор

Иногда нам может понадобиться поменять местами две переменные в нашем коде.

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

2. Простой способ: использование временной переменной

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

Object a, b;
Object temp;
temp = a;
a = b;
b = temp;

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

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

2.1. Почему бы не поменять метод?

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

public void swap(Object a, Object b)

К сожалению, это не будет работать в Java , так как ссылки на объекты копируются во время вызова метода.

Если мы действительно хотим иметь метод подкачки, мы должны использовать класс-оболочку вокруг вашего объекта и поменять местами объект, содержащийся в оболочке:

private class Wrapper {
public String string;
}

И метод обмена:

private static void swap(Wrapper a, Wrapper b) {
String temp = b.string;
b.string = a.string;
a.string = temp;
}

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

3. Без временной переменной

Если наши переменные имеют примитивные типы, мы можем найти способы поменять их местами без временных переменных.

Давайте посмотрим несколько примеров.

3.1. Использование арифметических операций

Мы можем использовать математику для замены переменных без временных переменных несколькими способами. В следующих примерах предположим, что мы хотим поменять местами два целых числа a=5 и b=10 .

Мы можем использовать сложения и вычитания для замены:

a = a + b;  // a = 15
b = a - b; // b = 5
a = a - b; // a = 10

Или мы можем использовать умножение и деление:

a = a * b;  // a = 50
b = a / b; // b = 5
a = a / b; // a = 10

Мы должны иметь в виду, что этот метод не работает, если какое-либо из чисел равно 0, поскольку первая операция приведет к сохранению нуля, что сделает остальную часть алгоритма бесполезной. Более того, если b = 0, будет выдано исключение ArithmeticException из-за деления на ноль.

Мы также должны позаботиться о емкости примитивов, поскольку сложение/умножение может привести к числам, превышающим максимальное значение типа примитива. Это может привести к ошибкам после замены без выдачи каких-либо исключений.

Например, если a = Integer.MAX_VALUE, то до замены a=2147483647 и b=10 и после замены a=10 , b=-1.

Если мы работаем с типами данных char , byte или short , требуется явное приведение типов, поскольку результатом арифметических операций является значение типа int по крайней мере в Java:

a = (char)(a + b);
b = (char)(a - b);
a = (char)(a - b);

3.2. Использование логических операций

Если мы работаем с целочисленными типами данных (например, char, short, byte, int, long ), мы можем использовать побитовый оператор исключающее ИЛИ (XOR). Оператор «^» будет обрабатывать побитовую операцию XOR для всех битов наших переменных:

a = a ^ b;  // a = 1111 (15)
b = a ^ b; // b = 1010 (5)
a = a ^ b; // a = 0101 (10)

Мы должны знать, что, как и в случае с арифметическими операторами, побитовый оператор XOR возвращает как минимум тип данных int . Итак, мы должны привести результат XOR для каждой строки, если мы работаем с символами, байтами или короткими переменными.

3.3. Однострочный вариант

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

b = (a + b)(a = b);
a += b – (b = a);
a = a * b / (b = a);
a = a ^ b ^ (b = a);

Это работает, потому что выражения оцениваются с учетом приоритета операторов. Если изначально a = 5 и b = 10, последнее выражение эквивалентно a = 5 ^ 10 ^ (b = 5) . Первая операция ( 5 ^ 10 ) — это в точности первая строка многострочного алгоритма, затем мы присваиваем 5 b (скобки имеют приоритет) и, наконец, вычисляем 15 ^ 5 , что является ровно третьей строкой алгоритма.

4. Анализ производительности

Мы только что увидели, что в Java есть несколько способов поменять местами две переменные, но какой из них более эффективен? Чтобы получить представление о производительности каждого алгоритма, мы выполнили циклы методов замены переменных и измерили время, необходимое для замены двух переменных 100 000 раз. Мы запускали тест 10 раз, чтобы рассчитать среднее время выполнения каждого алгоритма. Вот результаты:

./70e512e48166a82fd49c43863db1e9c0.png

Абсолютное время здесь не важно, так как оно зависит от машины, на которой выполняется тест. Мы только видим, что одни алгоритмы медленнее других. Это особенно верно для умножения/деления, которое значительно медленнее, либо в его однострочной версии. Напротив, алгоритм XOR наиболее эффективен как в многострочном, так и в однострочном вариантах.

Обмен объектами с временной переменной также весьма эффективен, что вполне понятно, поскольку в этом случае манипулируются только указатели.

5. Вывод

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

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

Как всегда, исходный код всех примеров доступен на GitHub .