1. Обзор
Операторы используются в языке Java для работы с данными и переменными.
В этом руководстве мы рассмотрим побитовые операторы и то, как они работают в Java.
2. Побитовые операторы
Побитовые операторы работают с двоичными цифрами или битами входных значений. Мы можем применить их к целочисленным типам — long, int, short, char
и byte.
Прежде чем изучать различные побитовые операторы, давайте сначала разберемся, как они работают.
Побитовые операторы работают с двоичным эквивалентом десятичных чисел и выполняют над ними операции побитно в соответствии с данным оператором:
- Сначала операнды преобразуются в их двоичное представление.
- Далее оператор применяется к каждому двоичному числу и вычисляется результат
- Наконец, результат преобразуется обратно в десятичное представление.
Давайте разберемся на примере; возьмем два целых числа:
int value1 = 6;
int value2 = 5;
Далее применим к этим числам побитовый оператор ИЛИ:
int result = 6 | 5;
Для выполнения этой операции сначала будет вычислено двоичное представление этих чисел:
Binary number of value1 = 0110
Binary number of value2 = 0101
Затем операция будет применена к каждому биту. Результат возвращает новое двоичное число:
0110
0101
-----
0111
Наконец, результат 0111
будет преобразован обратно в десятичное число, равное 7
:
result : 7
Побитовые операторы далее классифицируются как побитовые логические и побитовые операторы сдвига. Давайте теперь пройдемся по каждому типу.
3. Побитовые логические операторы
Побитовые логические операторы: AND(&), OR(|), XOR(^) и NOT(~).
3.1. Побитовое ИЛИ
(|)
Оператор ИЛИ сравнивает каждую двоичную цифру двух целых чисел и возвращает 1, если одно из них равно 1.
Это похоже на || логический оператор, используемый с логическими значениями. При сравнении двух логических значений результат истинен
, если истинно любое из них .
Точно так же выход равен 1, когда любой из них равен 1.
Мы видели пример этого оператора в предыдущем разделе:
@Test
public void givenTwoIntegers_whenOrOperator_thenNewDecimalNumber() {
int value1 = 6;
int value2 = 5;
int result = value1 | value2;
assertEquals(7, result);
}
Давайте посмотрим на бинарное представление этой операции:
0110
0101
-----
0111
Здесь мы видим, что использование ИЛИ, 0 и 0 приведет к 0, а любая комбинация хотя бы с 1 приведет к 1.
3.2. Побитовое И
(&)
Оператор И сравнивает каждую двоичную цифру двух целых чисел и возвращает 1, если оба равны 1, иначе возвращает 0.
Это похоже на оператор && с логическими
значениями. Когда значения двух логических значений
истинны , результат операции && истинен
.
Давайте используем тот же пример, что и выше, за исключением того, что теперь мы используем оператор & вместо | оператор:
@Test
public void givenTwoIntegers_whenAndOperator_thenNewDecimalNumber() {
int value1 = 6;
int value2 = 5;
int result = value1 & value2;
assertEquals(4, result);
}
Давайте также посмотрим бинарное представление этой операции:
0110
0101
-----
0100
0100
равно 4
в десятичном виде, следовательно, результат:
result : 4
3.3. Побитовое исключающее ИЛИ (^)
Оператор XOR сравнивает каждую двоичную цифру двух целых чисел и возвращает 1, если оба сравниваемых бита различны. Это означает, что если биты обоих целых чисел равны 1 или 0, результатом будет 0; в противном случае результатом будет 1:
@Test
public void givenTwoIntegers_whenXorOperator_thenNewDecimalNumber() {
int value1 = 6;
int value2 = 5;
int result = value1 ^ value2;
assertEquals(3, result);
}
И двоичное представление:
0110
0101
-----
0011
0011
равно 3 в десятичном виде, следовательно, результат:
result : 3
3.4. Побитовое дополнение (~)
Побитовый оператор Not или Complement просто означает отрицание каждого бита входного значения. Он принимает только одно целое число и эквивалентен ! оператор.
Этот оператор изменяет каждую двоичную цифру целого числа, что означает, что все 0 становятся 1, а все 1 становятся 0. Оператор ! оператор работает аналогично для логических
значений: он меняет логические
значения с истинного
на ложное
и наоборот.
Теперь разберемся на примере, как найти дополнение десятичного числа.
Сделаем дополнение value1 = 6:
@Test
public void givenOneInteger_whenNotOperator_thenNewDecimalNumber() {
int value1 = 6;
int result = ~value1;
assertEquals(-7, result);
}
Значение в двоичном формате:
value1 = 0000 0110
Применяя оператор дополнения, результатом будет:
0000 0110 -> 1111 1001
Это дополнение до единицы десятичного числа 6. А поскольку первый (крайний левый) бит равен 1 в двоичном формате, это означает, что знак отрицательного значения для сохраняемого числа.
Теперь, поскольку числа хранятся в виде дополнения до 2, сначала нам нужно найти его дополнение до 2, а затем преобразовать полученное двоичное число в десятичное число:
1111 1001 -> 0000 0110 + 1 -> 0000 0111
Наконец, 0000 0111 — это 7 в десятичном виде. Поскольку бит знака был равен 1, как упоминалось выше, результирующий ответ:
result : -7
3.5. Таблица побитовых операторов
Давайте суммируем результаты операторов, которые мы видели до сих пор, в сравнительной таблице:
A B A|B A&B A^B ~A
0 0 0 0 0 1
1 0 1 0 1 0
0 1 1 0 1 1
1 1 1 1 0 0
4. Операторы побитового сдвига
Операторы двоичного сдвига сдвигают все биты входного значения влево или вправо в зависимости от оператора сдвига.
Давайте посмотрим на синтаксис этих операторов:
value <operator> <number_of_times>
Левая часть выражения — это целое число, которое сдвигается, а правая часть выражения обозначает, сколько раз его нужно сдвинуть.
Операторы побитового сдвига далее классифицируются как операторы побитового сдвига влево и побитового сдвига вправо.
4.1. Сдвиг влево со знаком <<
Оператор сдвига влево сдвигает биты влево на количество раз, указанное в правой части операнда. После сдвига влево пустое место справа заполняется 0.
Еще один важный момент, который следует отметить, заключается в том, что сдвиг числа на единицу эквивалентен умножению его на 2, или, вообще говоря, сдвиг числа влево на n
позиций эквивалентен умножению на 2^ n
.
Возьмем значение 12 в качестве входного значения.
Теперь сдвинем его на 2 позиции влево (12 <<2) и посмотрим, что получится в итоге.
Двоичный эквивалент числа 12 равен 00001100. После сдвига влево на 2 разряда получается 00110000, что эквивалентно 48 в десятичном виде:
@Test
public void givenOnePositiveInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
int value = 12;
int leftShift = value << 2;
assertEquals(48, leftShift);
}
Это работает аналогично для отрицательного значения:
@Test
public void givenOneNegativeInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
int value = -12;
int leftShift = value << 2;
assertEquals(-48, leftShift);
}
4.2. Сдвиг вправо со знаком >>
Оператор сдвига вправо сдвигает все биты вправо. Пустое место в левой части заполняется в зависимости от введенного числа:
- Когда входное число отрицательное, где крайний левый бит равен 1, пустые места будут заполнены 1
- Когда входное число положительное, где крайний левый бит равен 0, пустые места будут заполнены 0
Давайте продолжим пример, используя 12 в качестве входных данных.
Теперь сдвинем его на 2 места вправо (12 >> 2) и посмотрим, что получится в итоге.
Входное число положительное, поэтому после сдвига вправо на 2 знака результат будет 0011, что равно 3 в десятичном виде:
@Test
public void givenOnePositiveInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
int value = 12;
int rightShift = value >> 2;
assertEquals(3, rightShift);
}
Кроме того, для отрицательного значения:
@Test
public void givenOneNegativeInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
int value = -12;
int rightShift = value >> 2;
assertEquals(-3, rightShift);
}
4.3. Беззнаковый сдвиг вправо >>>
Этот оператор очень похож на знаковый оператор сдвига вправо. Единственное отличие состоит в том, что пустые места слева заполняются 0 независимо от того, положительное это число или отрицательное. Поэтому результатом всегда будет положительное целое число.
Давайте сдвинем вправо то же самое значение 12:
@Test
public void givenOnePositiveInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
int value = 12;
int unsignedRightShift = value >>> 2;
assertEquals(3, unsignedRightShift);
}
А теперь отрицательное значение:
@Test
public void givenOneNegativeInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
int value = -12;
int unsignedRightShift = value >>> 2;
assertEquals(1073741821, unsignedRightShift);
}
5. Разница между побитовыми и логическими операторами
Есть несколько различий между побитовыми операторами, которые мы здесь обсуждали, и более известными логическими операторами.
Во- первых, логические операторы работают с логическими
выражениями и возвращают логические
значения ( истина
или ложь),
тогда как побитовые операторы работают с двоичными цифрами целых значений ( long, int, short, char
и byte
) и возвращают целое число.
Кроме того, логические операторы всегда оценивают первое логическое
выражение и, в зависимости от его результата и используемого оператора, могут оценивать или не оценивать второе. С другой стороны, побитовые операторы всегда оценивают оба операнда .
Наконец, логические операторы используются для принятия решений на основе нескольких условий, в то время как побитовые операторы работают с битами и выполняют побитовые операции.
6. Варианты использования
Некоторые потенциальные варианты использования побитовых операторов:
- Коммуникационные стеки, в которых отдельные биты в заголовке, прикрепленном к данным, обозначают важную информацию.
- Во встроенных системах для установки/очистки/переключения только одного бита определенного регистра без изменения остальных битов.
- Чтобы зашифровать данные для обеспечения безопасности с помощью оператора XOR
- При сжатии данных путем преобразования данных из одного представления в другое, чтобы уменьшить объем используемого пространства.
7. Заключение
В этом руководстве мы узнали о типах побитовых операторов и о том, чем они отличаются от логических операторов. Мы также видели некоторые потенциальные варианты их использования.
Все примеры кода в этой статье доступны на GitHub .