JavaRush /Java блог /Random UA /Побітові операції в Java

Побітові операції в Java

Стаття з групи Random UA
Тобі, напевно, знайоме слово “біт”. Якщо ж ні, давай познайомимося з ним:) Біт - мінімальна одиниця виміру інформації в комп'ютері. Його назва походить від англійської " binary digit " - "двійкове число". Біт може бути виражений одним із двох чисел: 1 або 0. Існує спеціальна система числення, заснована на одиницях та нулях - двійкова. Не заглиблюватимемося в нетрі математики і відзначимо лише, що будь-яке число в Java можна конвертувати в його двійкову форму. Для цього потрібно використовувати класи-обгортки. Побітові операції - 1Наприклад, ось як можна зробити це для числа int:
public class Main {

   public static void main(String[] args) {

       int x = 342;
       System.out.println(Integer.toBinaryString(x));
   }
}
Виведення в консоль:

101010110
1010 10110 (я додав пробіл для зручності читання) - це число 342 у двійковій системі. Ми фактично розділабо це число на окремі біти – нулі та одиниці. Саме з ними ми можемо виконувати операції, що називаються побітовими.
  • ~- Побітовий оператор "НЕ".

Він працює дуже просто: проходить по кожному біту нашого числа і змінює його значення на протилежне: нулі – на одиниці, одиниці – на нулі. Якщо ми застосуємо його до нашого числа 342, ось що вийде: 101010110 - число 342 у двійковій системі 010101001 - результат виразу ~342 Але так як змінна типу int займає 4 байти, тобто. 32 біта, насправді число в змінній зберігається як: 00000000 00000000 00000001 01010110- Число 342 в змінній типу int в java 11111111 11111111 11111110 10101001- результат виразу ~342 в java Спробуємо виконати це на практиці:
public class Main {

   public static void main(String[] args) {

       int x = 342;
       System.out.println(Integer.toBinaryString(~x));
   }
}
Виведення в консоль:
11111111111111111111111010101001
  • &- Побітовий оператор "І"

Він, як бачиш, досить схожий на написання на логічний “І” ( &&). Оператор &&, як ти пам'ятаєш, повертає trueтільки якщо обидва операнди є істинними. Побітовий &працює так: він порівнює два числа по бітах. Результатом цього порівняння є третє число. Наприклад, візьмемо числа 277 і 432: 100010101 - число 277 у двійковій формі 110110000 - число 432 у двійковій формі Далі оператор &порівнює перший біт верхнього числа з першим бітом нижнього. Оскільки це оператор “І”, то результат дорівнюватиме 1 тільки в тому випадку, якщо обидва біти дорівнюють 1. У всіх інших випадках результатом буде 0. 100010101 110110000 & _______________ 100010000 — результат роботи& Ми порівнюємо спочатку перші біти двох чисел один з одним, потім другі біти, треті тощо. Як бачиш, тільки у двох випадках обидва біти в числах дорівнювали 1 (перший і п'ятий за рахунком біти). Результатом решти порівнянь став 0. Тому в результаті у нас вийшло число 100010000. У десятковій системі йому відповідає число 272. Давай перевіримо:
public class Main {

   public static void main(String[] args) {
       System.out.println(277&432);
   }
}
Виведення в консоль:

272
  • |- Побітове "АБО". Принцип роботи той самий - порівнюємо два числа по бітах. Тільки тепер якщо хоча б один з бітів дорівнює 1, результат дорівнюватиме 1. Подивимося на тих же числах - 277 і 432:
100010101 | 110110000 _______________ 110110101 — результат роботи | Тут уже результат інший: нулями залишабося тільки ті біти, які в обох числах були нулями. Результат роботи - число 110110101. У десятковій системі йому відповідає число 437. Перевіримо:
public class Main {

   public static void main(String[] args) {
       System.out.println(277|432);
   }
}
Виведення в консоль:

437
Ми всі порахували правильно! :)
  • ^- побітове що виключає "АБО" (також відомо як XOR)
З таким оператором ми ще не стикалися. Але нічого складного у ньому немає. Він схожий на звичайне "або". Різниця в одному: звичайне "або" повертає true, якщо хоча б один операнд є істинним. Але не обов'язково один – якщо обидва будуть true– то й результат true. А ось виключне "або" повертає trueтільки якщо один із операндів є істинним. Якщо істинні обидва операнда, звичайне "або" поверне true("хоч би один істинний"), а от виключає або поверне false. Тому він і називається таким, що виключає. Знаючи принцип попередніх побітових операцій, ти, напевно, і сам зможеш легко виконати операцію 277^432. Але давай краще зайвий раз розберемося разом :) 100010101 ^ 110110000 _______________ 010100101 - результат роботи^ Ось наш результат. Ті біти, які були обох числах однаковими, повернули 0 (не спрацювала формула “один з”). А ось ті, які утворювали пару 0-1 чи 1-0, у результаті перетворабося на одиницю. У результаті ми отримали число 010100101. У десятковій системі відповідає число 165. Давай подивимося, чи правильно ми порахували:
public class Main {

   public static void main(String[] args) {
       System.out.println(277^432);
   }
}
Виведення в консоль:

165
Супер! Все саме так, як ми й думали :) Тепер настав час познайомитися з операціями, які називають бітовими зрушеннями. Назва, в принципі, каже сама за себе. Ми візьмемо якесь число і рухатимемо його біти вліво і вправо :) Давай подивимося як це виглядає:

Зрушення вліво

Зсув бітів вліво позначається знаком << Приклад:
public class Main {

   public static void main(String[] args) {
       int x = 64;// Значення
       int y = 3;//кількість

       int z = (x << y);
       System.out.println(Integer.toBinaryString(x));
       System.out.println(Integer.toBinaryString(z));
   }
}
У цьому прикладі число x=64називається значенням. Саме його биті ми зрушуватимемо. Зрушувати біти ми будемо вліво (це можна визначити за напрямом знака <<) У двійковій системі число 64 = 1000000 Число y=3називається кількістю. Кількість відповідає питанням “на скільки біт вправо/вліво треба зрушити біти числа x” У прикладі ми зрушуватимемо їх у 3 біта вліво. Щоб процес зсуву був зрозуміліший, подивимося на картинці. У прикладі використовуються числа типу int. IntТи займають у пам'яті комп'ютера 32 біти. Ось так виглядає наше початкове число 64: Побітові операції - 2А тепер ми, у буквальному значенні слова, беремо кожен з наших бітів і зрушуємо вліво на 3 осередки: Побітові операції - 3Ось що в нас вийшло. Як бачиш, всі наші біти зрушабо, а з-за меж діапазону додалися ще 3 нулі. 3 - тому що ми робабо зрушення на 3. Якби ми зрушували на 10, додалося б 10 нулів. Таким чином, вираз x << yозначає "зрушити біти числа хна y осередків вліво". Результатом нашого виразу стало число 1000000000, яке в десятковій системі дорівнює 512. Перевіримо:
public class Main {

   public static void main(String[] args) {
       int x = 64;// Значення
       int y = 3;//кількість

       int z = (x << y);
       System.out.println(z);
   }
}
Виведення в консоль:

512
Все вірно! Теоретично, біти можна зрушувати до безкінечності. Але оскільки у нас число int, у розпорядженні є всього 32 осередки. У тому числі 7 вже зайняті числом 64 (1000000). Тому якщо ми зробимо, наприклад, 27 зрушень ліворуч, наша єдина одиниця вийде за межі діапазону і затремтить. Залишаться лише нулі!
public class Main {

   public static void main(String[] args) {
       int x = 64;// Значення
       int y = 26;//кількість

       int z = (x << y);
       System.out.println(z);
   }
}
Виведення в консоль:

0
Як ми й припускали, одиниця вийшла за межі 32 осередків-бітів і зникла. У нас вийшло 32-бітове число, що складається з одних нулів. Побітові операції - 4Природно, у десятковій системі йому відповідає 0. Просте правило для запам'ятовування зрушень вліво: При кожному зрушенні вліво виконується множення числа на 2. Наприклад, спробуємо без картинок з бітами порахувати результат виразу. Нам потрібно тричі помножити число 111111111 на 2 111111111 << 3 . 888888888. Давай напишемо код і перевіримо:
public class Main {

   public static void main(String[] args) {
       System.out.println(111111111 << 3);
   }
}
Виведення в консоль:

888888888

Зрушення праворуч

Вони позначаються знаком >>. Роблять те саме, тільки в інший бік! :) Не винаходитимемо велосипед і спробуємо зробити це з тим же числом int 64.
public class Main {

   public static void main(String[] args) {
       int x = 64;// Значення
       int y = 2;//кількість

       int z = (x >> y);
       System.out.println(z);
   }
}
Побітові операції - 5Побітові операції - 6В результаті зсуву на 2 вправо два крайні нулі нашого числа вийшли за межі діапазону і затерлися. У нас вийшло число 10000, якому в десятковій системі відповідає число 16 Висновок в консоль:

16
Просте правило для запам'ятовування зрушень праворуч: При кожному зрушенні праворуч виконується розподіл на два з відкиданням будь-якого залишку. Наприклад, 35 >> 2 означає, що нам потрібно 2 рази розділити 35 на 2, відкидаючи залишки 35/2 = 17(відкинули залишок 1) 17:2 = 8(відкинули залишок 1) Отже, 35 >> 2має бути 8. Перевіряємо:
public class Main {

   public static void main(String[] args) {
       System.out.println(35 >> 2);
   }
}
Виведення в консоль:

8

Пріоритет операцій на Java

У процесі написання чи читання коду тобі часто траплятимуться висловлювання, у яких одночасно виконуються кілька операцій. Дуже важливо розуміти, в якому порядку їх буде виконано, інакше результат може бути несподіваним. Оскільки операцій у Java багато, всі вони були виділені до спеціальної таблиці:

Operator Precedence

Operators Precedence
postfix expr++ expr--
unary ++expr --expr +expr ~ !
Multiplicative * / %
additive + -
shift << >> >>>
relational < > <= >=instanceof
Equality == !=
bitwise AND &
bitwise exclusive OR ^
bitwise inclusive OR |
logical AND &&
logical OR ||
ternary ? :
assignment = += -= *= /= %= &= ^= |= <<= >>= >>>=
Усі операції виконуються ліворуч, проте з урахуванням свого пріоритету. Наприклад, якщо ми пишемо: int x = 6 - 4/2; спочатку буде виконано операцію поділу (4/2). Хоч вона і йде другою за рахунком, але в неї вищий пріоритет. Круглі або квадратні дужки змінюють пріоритет на максимальний. Це ти, напевно, пам'ятаєш ще зі школи. Наприклад, якщо додати їх до виразу: int x = (6 - 4)/2; першим виконається саме віднімання, оскільки воно обчислюється у дужках. У логічного оператора &&пріоритет є досить низьким, що видно з таблиці. Тому найчастіше він виконуватиметься останнім. Наприклад: boolean x = 6 - 4/2 > 3 && 12*12 <= 119; Цей вираз виконуватиметься так:
  • 4/2 = 2

    boolean x = 6 - 2 > 3 && 12*12 <= 119;
  • 12*12 = 144

    boolean x = 6 - 2 > 3 && 144 <= 119;
  • 6-2 = 4

    boolean x = 4 > 3 && 144 <= 119;
  • Далі будуть виконані оператори порівняння:

    4 > 3 = true

    boolean x = true && 144 <= 119;
  • 144 <= 119 = false

    boolean x = true && false;
  • І, нарешті, останнім буде виконано оператор "І" &&.

    boolean x = true && false;

    boolean x = false;

    Оператор складання ( +), наприклад, має вищий пріоритет, ніж оператор порівняння !=(“не дорівнює”);

    Тому у виразі:

    boolean x = 7 != 6+1;

    спочатку буде виконано операцію 6+1, потім перевірку 7!=7 (false), а в кінці — привласнення результату falseзмінної x. У присвоювання взагалі найменший пріоритет із усіх операцій — подивися у таблиці.

Фух! Лекція у нас вийшла велика, але ти впорався! Якщо ти не до кінця зрозумів якісь частини цієї та попередньої лекцій — не хвилюйся, ми ще не раз торкнемося даних тим у майбутньому. Ось тобі кілька корисних посилань:
  • Логічні оператори - лекція JavaRush про логічні операції. Ми до них ще нескоро дійдемо, але почитати можна вже зараз, не буде шкоди
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ