Поиск…


замечания

  • В отличие от C / C ++, Java полностью нейтральна по отношению к базовому аппарату. По умолчанию вы не становитесь большим или маленьким поведением; вы должны явно указать, какое поведение вы хотите.

  • Тип byte подписан, диапазон от -128 до +127. Чтобы преобразовать значение байта в его беззнаковый эквивалент, замаскируйте его с помощью 0xFF следующим образом: (b & 0xFF) .

Упаковка / распаковка значений в виде фрагментов

Обычно производительность памяти сводит несколько значений в одно примитивное значение. Это может быть полезно для передачи различной информации в одну переменную.

Например, можно упаковать 3 байта - например, цветовой код в RGB - в один int.

Упаковка значений

// Raw bytes as input
byte[] b = {(byte)0x65, (byte)0xFF, (byte)0x31};

// Packed in big endian: x == 0x65FF31
int x = (b[0] & 0xFF) << 16  // Red
      | (b[1] & 0xFF) <<  8  // Green
      | (b[2] & 0xFF) <<  0; // Blue

// Packed in little endian: y == 0x31FF65
int y = (b[0] & 0xFF) <<  0
      | (b[1] & 0xFF) <<  8
      | (b[2] & 0xFF) << 16;

Распаковка значений

// Raw int32 as input
int x = 0x31FF65;

// Unpacked in big endian: {0x65, 0xFF, 0x31}
byte[] c = {
    (byte)(x >> 16),
    (byte)(x >>  8),
    (byte)(x & 0xFF)
};

// Unpacked in little endian: {0x31, 0xFF, 0x65}
byte[] d = {
    (byte)(x & 0xFF),
    (byte)(x >>  8),
    (byte)(x >> 16)
};

Проверка, настройка, очистка и переключение отдельных битов. Использование длинной битовой маски

Предполагая, что мы хотим изменить бит n целочисленного примитива, i (байт, короткий, char, int или long):

(i & 1 << n) != 0 // checks bit 'n'
i |= 1 << n;      // sets bit 'n' to 1
i &= ~(1 << n);   // sets bit 'n' to 0
i ^= 1 << n;      // toggles the value of bit 'n'

Использование long / int / short / byte в качестве битовой маски:

public class BitMaskExample {
    private static final long FIRST_BIT = 1L << 0;
    private static final long SECOND_BIT = 1L << 1;
    private static final long THIRD_BIT = 1L << 2;
    private static final long FOURTH_BIT = 1L << 3;
    private static final long FIFTH_BIT = 1L << 4;
    private static final long BIT_55 = 1L << 54;

    public static void main(String[] args) {
        checkBitMask(FIRST_BIT | THIRD_BIT | FIFTH_BIT | BIT_55);
    }

    private static void checkBitMask(long bitmask) {
        System.out.println("FIRST_BIT: " + ((bitmask & FIRST_BIT) != 0));
        System.out.println("SECOND_BIT: " + ((bitmask & SECOND_BIT) != 0));
        System.out.println("THIRD_BIT: " + ((bitmask & THIRD_BIT) != 0));
        System.out.println("FOURTh_BIT: " + ((bitmask & FOURTH_BIT) != 0));
        System.out.println("FIFTH_BIT: " + ((bitmask & FIFTH_BIT) != 0));
        System.out.println("BIT_55: " + ((bitmask & BIT_55) != 0));
    }
}

Печать

FIRST_BIT: true
SECOND_BIT: false
THIRD_BIT: true
FOURTh_BIT: false
FIFTH_BIT: true
BIT_55: true

который соответствует этой маске мы прошли в качестве checkBitMask параметра: FIRST_BIT | THIRD_BIT | FIFTH_BIT | BIT_55 .

Выражая силу 2

Для выражения степени 2 (2 ^ n) целых чисел можно использовать операцию бит-сдвига, которая позволяет явно указать n .

Синтаксис в основном:

int pow2 = 1<<n;

Примеры:

int twoExp4 = 1<<4; //2^4
int twoExp5 = 1<<5; //2^5
int twoExp6 = 1<<6; //2^6
...
int twoExp31 = 1<<31; //2^31

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

int twoExp4 = 0x10; //hexadecimal
int twoExp5 = 0x20; //hexadecimal
int twoExp6 = 64; //decimal
...
int twoExp31 = -2147483648; //is that a power of 2?

Простым методом вычисления мощности int 2 будет

int pow2(int exp){
    return 1<<exp;
}

Проверка того, является ли число мощностью 2

Если целое число x равно 2, устанавливается только один бит, тогда как x-1 имеет все биты, установленные после этого. Например: 4 равно 100 и 3 равно 011 как двоичное число, которое удовлетворяет вышеупомянутому условию. Ноль не равен 2 и должен быть проверен явно.

boolean isPowerOfTwo(int x)
{
    return (x != 0) && ((x & (x - 1)) == 0);
}

Использование левого и правого сдвига

Предположим, у нас есть три вида разрешений: READ , WRITE и EXECUTE . Каждое разрешение может варьироваться от 0 до 7. (Предположим, что система с четырьмя битами)

RESOURCE = READ WRITE EXECUTE (12-разрядное число)

RESOURCE = 0100 0110 0101 = 4 6 5 (12-разрядное число)

Как мы можем получить разрешения (12-разрядного номера), установленные выше (12-разрядное число)?

0100 0110 0101

0000 0000 0111 (&)

0000 0000 0101 = 5

Таким образом, мы можем получить разрешения EXECUTE RESOURCE . Теперь, что, если мы хотим получить READ- разрешения RESOURCE ?

0100 0110 0101

0111 0000 0000 (&)

0100 0000 0000 = 1024

Правильно? Вероятно, вы это принимаете? Но разрешения приведены в 1024. Мы хотим получить только разрешения READ для ресурса. Не волнуйтесь, поэтому у нас были операторы смены. Если мы увидим, разрешения READ на 8 бит превысят фактический результат, поэтому, если применить некоторый оператор сдвига, который приведет к разрешению READ до самого правильного результата? Что делать, если мы это сделаем:

0100 0000 0000 >> 8 => 0000 0000 0100 (потому что это положительное число, замененное на 0, если вы не заботитесь о знаке, просто используйте беззнаковый оператор сдвига вправо)

Теперь у нас есть разрешения READ, которые равны 4.

Теперь, например, нам предоставлены разрешения READ , WRITE , EXECUTE для RESOURCE , что мы можем сделать, чтобы сделать разрешения для этого РЕСУРСА ?

Давайте сначала рассмотрим пример двоичных разрешений. (Все еще предполагая систему с 4-разрядными номерами)

READ = 0001

WRITE = 0100

EXECUTE = 0110

Если вы думаете, что мы просто сделаем это:

READ | WRITE | EXECUTE , вы несколько правы, но не совсем. Смотрите, что произойдет, если мы будем выполнять READ | НАПИСАТЬ | ВЫПОЛНИТЬ

0001 | 0100 | 0110 => 0111

Но разрешения фактически представлены (в нашем примере) как 0001 0100 0110

Итак, чтобы сделать это, мы знаем, что READ размещен на 8 бит позади, WRITE помещается на 4 бита, а PERMISSIONS помещается последним. Система номеров, используемая для разрешений RESOURCE, на самом деле составляет 12 бит (в нашем примере). Он может (будет) отличаться в разных системах.

(READ << 8) | (WRITE << 4) | (EXECUTE)

0000 0000 0001 << 8 (READ)

0001 0000 0000 (сдвиг влево на 8 бит)

0000 0000 0100 << 4 (WRITE)

0000 0100 0000 (сдвиг влево на 4 бита)

0000 0000 0001 (ВЫПОЛНИТЬ)

Теперь, если мы добавим результаты вышеперечисленного, это будет нечто подобное;

0001 0000 0000 (READ)

0000 0100 0000 (ЗАПИСЬ)

0000 0000 0001 (ВЫПОЛНИТЬ)

0001 0100 0001 (РАЗРЕШЕНИЯ)

Класс java.util.BitSet

Начиная с версии 1.7 существует класс java.util.BitSet, который обеспечивает простой и удобный интерфейс хранения и манипулирования битами:

final BitSet bitSet = new BitSet(8); // by default all bits are unset

IntStream.range(0, 8).filter(i -> i % 2 == 0).forEach(bitSet::set); // {0, 2, 4, 6}

bitSet.set(3); // {0, 2, 3, 4, 6}

bitSet.set(3, false); // {0, 2, 4, 6}

final boolean b = bitSet.get(3); // b = false

bitSet.flip(6); // {0, 2, 4}

bitSet.set(100); // {0, 2, 4, 100} - expands automatically

BitSet реализует Clonable и Serializable , а под капотом все значения бит хранятся в long[] words поле long[] words , которое автоматически расширяется.

Он также поддерживает целые логические операции and , or , xor , andNot :

bitSet.and(new BitSet(8));
bitSet.or(new BitSet(8));
bitSet.xor(new BitSet(8));
bitSet.andNot(new BitSet(8));

Подписанный беззнаковый сдвиг

В Java все примитивы числа подписаны. Например, int всегда представляет значения из [-2 ^ 31 - 1, 2 ^ 31], сохраняя первый бит для подписи значения - 1 для отрицательного значения, 0 для положительного.

Операторы основного сдвига >> и << являются операторами-операторами. Они сохранят знак ценности.

Но программисты часто используют номера для хранения значений без знака . Для int это означает смещение диапазона до [0, 2 ^ 32 - 1], чтобы иметь в два раза большее значение, чем с подписанным int.

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

                initial value:               4 (                                100)
     signed left-shift: 4 << 1               8 (                               1000)
    signed right-shift: 4 >> 1               2 (                                 10)
 unsigned right-shift: 4 >>> 1               2 (                                 10)
                initial value:              -4 (   11111111111111111111111111111100)
    signed left-shift: -4 << 1              -8 (   11111111111111111111111111111000)
   signed right-shift: -4 >> 1              -2 (   11111111111111111111111111111110)
unsigned right-shift: -4 >>> 1      2147483646 (    1111111111111111111111111111110)

Почему нет <<< ?

Это исходит из предполагаемого определения сдвига вправо. Когда он заполняет опустошенные места слева, нет никакого решения принять бит за знак. Как следствие, нет необходимости в двух разных операторах.

См. Этот вопрос для более детального ответа.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow