JavaRush /Курсы /Java Syntax Pro /Побитовые операции, часть 2

Побитовые операции, часть 2

Java Syntax Pro
8 уровень , 5 лекция
Открыта

1. Побитовый сдвиг влево

Также в Java есть 3 оператора побитового сдвига числа. Вы можете очень просто сдвинуть все биты числа на несколько позиций влево или вправо, если вам это, конечно, нужно.

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

a << b

Где, а — число, биты которого сдвигаем, а b — число, означающее, на сколько бит влево нужно сдвинуть биты числа a. При этом справа к числу добавляются нулевые биты.

Примеры:

Пример Результат
0b00000011 << 1
0b00000110
0b00000011 << 2
0b00001100
0b00000011 << 5
0b01100000
0b00000011 << 20
0b001100000000000000000000

Сдвиг на один разряд влево дает тот же эффект, что и умножение числа на 2.

Хотите умножить число на 16 ? 16 = это 24. Значит нужно сдвинуть число на 4 разряда влево


2. Побитовый сдвиг вправо

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

a >> b

Где, а — число, биты которого сдвигаем, а b — число, означающее, на сколько бит вправо нужно сдвинуть биты числа a.

Примеры:

Пример Результат
0b11000011 >> 1
0b01100001
0b11000011 >> 2
0b00110000
0b11000011 >> 5
0b00000110
0b11000011 >> 20
0b00000000

Сдвиг на один разряд вправо дает тот же эффект, что и деление числа на 2 нацело.

При этом слева к числу добавляются нулевые биты, но не всегда!

Важно!

Самый левый бит числа называется битом знака: хранит 0, если число положительное и 1, если число отрицательное.

При сдвиге битов числа вправо, бит знака тоже сместится, и знак числа потеряется. Поэтому для отрицательных чисел (у которых самый левый бит равен 1), этот бит специально сохраняется. При сдвиге бит числа вправо, слева число дополняется битом 0, если самый левый бит был 0, и битом 1, если самый левый бит был 1.

Но в примере выше этого не происходит. Почему? Все дело в том, что целочисленные литералы имеют тип int и 0b11111111 на самом деле значит 0b00000000000000000000000011111111. Т.е. самый левый бит равен нулю.

Многих программистов расстраивает такое поведение сдвига вправо, и они хотели бы, чтобы число всегда дополнялось нулевыми битами. Поэтому в Java добавили еще один оператор сдвига вправо.

Записывается он так:

a >>> b

Где, а — число, биты которого сдвигаем, а b — число, означающее, на сколько бит вправо нужно сдвинуть биты числа a. Этот оператор всегда дописывает слева нулевые биты, независимо от того, какой бит знака был у числа a.



3. Работа с флагами

На основе побитовых операций и операций сдвига программисты реализовали чуть ли не целое новое направление — работа с флагами.

Во времена, когда памяти в компьютерах было очень мало, было очень популярно впихивать в одно число кучу информации. При этом число рассматривалось как массив бит: int — это 32 бита, long — 64 бита.

В такое число можно много всего записать, особенно если нужно хранить логическую информацию: true или false. Один long — это как массив boolean на 64 элемента. Такие биты назывались флагами и нуждались в операциях:

  • установить флаг
    (сделать определенный бит равным 1)
  • сбросить флаг
    (сделать определенный бит равным 0)
  • проверить флаг
    (проверить, чему равен определенный бит)

И вот как это делается с помощью побитовых операторов.

Установка флага

Чтобы установить определенный бит в 1, нужно выполнить операцию побитового ИЛИ между числом, в котором нужно установить этот бит и специальным числом, где только этот бит равен 1.

Например, вам нужно в числе 0b00001010 установить 5-й бит в 1, тогда нужно сделать так:

0b00001010 | 0b00010000 = 0b00011010

Если 5-й бит уже был установлен в единицу, ничего бы не поменялось.

В общем виде операцию установки флага можно записать так

a | (1 << b)

Где a — это число, внутри которого определенный бит устанавливается в 1. А b — это индекс устанавливаемого бита. Очень удобно использовать тут сдвиг влево – сразу видно, с каким битом мы работаем.

Сброс флага

Чтобы сбросить определенный бит в 0 и не потревожить другие биты, нужно выполнить операцию & между числом, в котором нужно сбросить бит в 0 и специальным числом, где все биты равны 1, кроме заданного.

Например, вам нужно в числе 0b00001010 установить 4-й бит в 0, тогда нужно сделать так:

0b00001010 & 0b11110111 = 0b00000010

Если 4-й бит уже был сброшен в ноль, ничего бы не поменялось.

В общем виде операцию сброса флага можно записать так

a & ~(1 << b)

Где a — это число, внутри которого сбрасывается в 0 определенный бит. А b — это индекс сбрасываемого бита.

Чтобы получить число, у которого все биты равны 1, а нужный нам равен нулю, мы сначала сдвигаем единицу на b позиций влево, а затем инвертируем биты числа с помощью побитовой операции НЕ.

Проверка флага

Кроме установки или сброса определенного флага, иногда бывает нужно просто проверить, установлен ли данный флаг – равен ли определенный бит 1. Это достаточно легко сделать с помощью побитовой операции &.

Например, вам нужно в числе 0b00001010 проверить, установлен ли 4-й бит в 1. Тогда нужно сделать так:

if ( (0b00001010 & 0b00001000) == 0b00001000 )

В общем виде операцию проверки флага можно записать так:

(a & (1 << b)) == (1 << b)

Где a — это число, внутри которого находится проверяемый бит. А b — это индекс проверяемого бита.


4. Шифрование

Побитовая операция XOR часто применяется программистами для самого простого шифрования. В общем виде операция шифрования выглядит так:

результат = число ^ пароль;

Где число — это данные, которые мы хотим зашифровать, пароль — специальное число, используемое в качестве «пароля» к данным, а результат — зашифрованное число.

число == (число ^ пароль) ^ пароль;

Все дело в том, что оператор XOR, дважды примененный к числу, дает исходное число, независимо от пароля.

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

оригинальное число = результат ^ пароль;

Пример:

class Solution
{
   public static int[] cript(int[] data, int password)
   {
     int[] result = new int[data.length];
     for (int i = 0; i <  data.length; i++)
       result[i] = data[i] ^ password;
     return result;
   }

   public static void main(String[] args)
   {
     int[] data =  {1, 3, 5, 7, 9, 11};
     int password = 199;

     // зашифровываем массив данных
     int[] encrypted = cript(data, password) ;
     System.out.println(Arrays.toString(encrypted));

     // расшифровываем массив данных
     int[] decrypted = cript(encrypted, password) ;
     System.out.println(Arrays.toString(decrypted));
   }
}

Комментарии (412)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Дмитрий Уровень 14
15 ноября 2025
a & ~(1 << b) не сразу понял это действие. Возьмём живые числа. a=10, b=2. Кстати 2 – это индекс в данном примере, в лекции умолчали. Берём 1 и смещаем на второй ИНДЕКС влево, получается 100, или 0b0100 (4 в десятичной). Проделываем « ~» над 0b0100 получаем 0b1011. Остается операция «И» между a и b. 10 равно 0b1010, ~(1 << b) равно 0b1011. 0b1010 & 0b1011 = 0b1010
Evgenii Petrov Уровень 10
25 октября 2025
21853 :( Задача про степень двойки
Владислав Уровень 10
19 октября 2025
Задача "Флаги "чисто по теории этой лекции: установка, снятие и проверка флага. Не тянет на медиум никак. А по задаче "Степень двойки" очень помогло видео CS50, где на одной из лекции были игры с лампами. По сути сдвигаем влево число в 8-ми битном двоичном формате, вот и степень двойки получается: 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
Grigoryvvv Уровень 10 Expert
19 октября 2025
19.10.2025 / 9 уровень
Александр Уровень 15 Expert
24 сентября 2025
Индекс бита считается от 0 справа на лево? или от единицы?
Grigoryvvv Уровень 10 Expert
19 октября 2025
Справа налево. Как и в массивах индекс бита считается от 0. Что бы поменять 4 бит на 1 нужно написать a | (1 << 3); 4му биту будет соответствовать индекс 3 (0, 1, 2, 3) Бит то 4й справа налево, а индекс у него считая от 0 справа на лево 3й. В последней задаче в Требованиях указано: В методе markForDeletion нужно установить в 1 биты 13, 21, 29 всем элементам santaFabrica. Это не правильно. Правильно было написать Требования: В методе markForDeletion нужно установить в 1 индексы битов 13, 21, 29 всем элементам santaFabrica. Или по другому. В методе markForDeletion нужно установить в 1 биты 14, 22, 30 всем элементам santaFabrica. Я в обсуждениях к задачам, когда не понятные моменты, пишу комментарии. Почитайте (открываете задачу, сверху нажимаете Обсуждения).
Андрей Руденко Уровень 21
21 сентября 2025
Вообще не понятно что написано. Слова вроде знакомые, а вместе как будто на китайском
Vlad Уровень 10
7 ноября 2025
как же тебя занесло на 21 уровень тогда? ))
Zhenya Volkov Уровень 30
17 сентября 2025
и снова чет вообще не понимаю чего от меня хотят даже в первой задаче. с горем пополам разобрался с первой. но вот прикол,вторую не засчитывало, ругалось на разные типы в одном выражении - int и boolean, хотя я написал ее сам ручками как в решении. потом психанул, нажал правильное решение и проверить - пропустило. зате символ за симвллом сравнил "правильное решение" и то что я с него списал на 100% - совпадают полностью. Что за х?ня, господа?
Valdimir Уровень 11
10 сентября 2025
Было сложно, но чертовски интересно 😅
Vadim Уровень 11
22 августа 2025
а че нет более полезных тем? или это база?
Anton Zorin Уровень 20
19 августа 2025
Плохо описана побитка, особенно примеры. Лучше спросить у ИИ.