предисловие
заоператор смены, многие люди чувствуют себя как знакомыми, так и незнакомыми. Знакомый, потому что оператор сдвига является одним из самых основных операторов, и он включен почти в каждый язык программирования; незнакомый, потому что его трудно использовать, если только он не используется в редких случаях, таких как стремление к экстремальной производительности. Откройте исходный код JDK, вы обнаружите, что оператор сдвига чрезвычайно распространен, и очень полезно понять его использование для чтения исходного кода.
Операция сдвига — это операция, которая обрабатывает данные как двоичное число и сдвигает их влево или вправо на несколько битов. В языке программирования Java есть три оператора сдвига, которые<<
(сдвиг влево),>>
(подписанный правый сдвиг) и>>>
(беззнаковый сдвиг вправо), эти три оператора могут работать только наlong
,int
,short
,byte
,char
на четыре основных целочисленных типа.
Оператор сдвига влево
оператор сдвига влево<<
После преобразования данных в двоичные числа,Сдвиньте несколько битов влево, отбросьте старшие биты и заполните младшие биты нулями. См. следующий пример:
public static void main(String[] args) {
int i = -1;
System.out.println("Before << , i's value is " + i);
System.out.println("i's binary string is " + Integer.toBinaryString(i));
i <<= 10;
System.out.println("After << , i's value is " + i);
System.out.println("i's binary string is " + Integer.toBinaryString(i));
}
Яваint
занимает 32 бита, поэтомуi = -1
Преобразуйте в двоичное число, а затем сдвиньте влево на 10 бит.В результате старшие 10 бит слева отбрасываются, а младшие 10 бит справа дополняются.0
, а затем преобразовать в десятичную, мы получаемi = -1024
результат.
Таким образом, вывод приведенного выше примера:
Before << , i's value is -1
i's binary string is 11111111111111111111111111111111
After << , i's value is -1024
i's binary string is 11111111111111111111110000000000
Подписанный оператор правой смены >>
Как мы все знаем, когда целое число в Java представляет отрицательное число, старший бит является битом знака, а положительное число0
, отрицательное число равно1
.>>
является оператором сдвига вправо со знаком после преобразования данных в двоичные числа,Сдвиньте несколько битов вправо, старший бит дополняет знаковый бит, а младший отбрасывает. При выполнении операции сдвига вправо для положительного числа она реализуется как дополнение высокого порядка.0
; Отрицательные числа дополняются1
. См. следующий пример:
public static void main(String[] args) {
// 对正数进行右移操作
int i1 = 4992;
System.out.println("Before >> , i1's value is " + i1);
System.out.println("i1's binary string is " + Integer.toBinaryString(i1));
i1 >>= 10;
System.out.println("After >> , i1's value is " + i1);
System.out.println("i1's binary string is " + Integer.toBinaryString(i1));
// 对负数进行右移操作
int i2 = -4992;
System.out.println("Before >> , i2's value is " + i2);
System.out.println("i2's binary string is " + Integer.toBinaryString(i2));
i2 >>= 10;
System.out.println("After >> , i2's value is " + i2);
System.out.println("i2's binary string is " + Integer.toBinaryString(i2));
}
В примереi1 = 4992
Преобразование в двоичное число, сдвинутое вправо на 10 бит, дает левое старшее 10-битное дополнение.0
, младшие 10 бит справа отбрасываются, а затем преобразуются в десятичные числа, чтобы получитьi1 = 4
результат. Так же,i2 = -4992
, сдвиг вправо на 10 бит, дополнение влево на 10 старших битов1
, младшие 10 бит справа отбрасываются, и мы получаемi2 = -5
результат.
Таким образом, вывод приведенного выше примера:
Before >> , i1's value is 4992
i1's binary string is 1001110000000
After >> , i1's value is 4
i1's binary string is 100
Before >> , i2's value is -4992
i2's binary string is 11111111111111111110110010000000
After >> , i2's value is -5
i2's binary string is 11111111111111111111111111111011
Беззнаковый оператор сдвига вправо >>>
беззнаковый оператор сдвига вправо>>>
и>>
Точно так же данные преобразуются в двоичное число, а затем сдвигаются вправо на несколько битов, разница в том, что независимо от того, отрицательное число или нет, результатСтаршие биты заполняются нулями, младшие отбрасываются. См. следующий пример:
public static void main(String[] args) {
int i3 = -4992;
System.out.println("Before >>> , i3's value is " + i3);
System.out.println("i3's binary string is " + Integer.toBinaryString(i3));
i3 >>>= 10;
System.out.println("After >>> , i3's value is " + i3);
System.out.println("i3's binary string is " + Integer.toBinaryString(i3));
}
такое же правоi3 = -4992
Выполните операцию, после преобразования его в двоичное число сдвиньте 10 бит вправо, и в результате получится дополнение старших 10 бит слева.0
, младшие 10 бит справа отбрасываются, а затем преобразуются в десятичные числа, чтобы получитьi3 = 4194299
результат.
Таким образом, вывод приведенного выше примера:
Before >>> , i3's value is -4992
i3's binary string is 11111111111111111110110010000000
After >>> , i3's value is 4194299
i3's binary string is 1111111111111111111011
Вы действительно понимаете?
Операции сдвига на коротких, байтовых, символьных
Рассмотрим следующий пример:
public static void main(String[] args) {
byte b = -1;
System.out.println("Before >> , b's value is " + b);
System.out.println("b's binary string is " + Integer.toBinaryString(b));
b >>>= 6;
System.out.println("After >> , b's value is " + b);
System.out.println("b's binary string is " + Integer.toBinaryString(b));
}
Яваbyte
Занимают 8 бит, по описанному выше принципу справаb = -1
После преобразования в двоичное число сдвиньте вправо на 6 бит и дополните старшие 6 бит слева0
, нижние правые биты отбрасываются, и результат должен бытьb = 3
.
Это действительно так? Давайте посмотрим на результат запуска примера:
Before >> , b's value is -1
b's binary string is 11111111111111111111111111111111
After >> , b's value is -1
b's binary string is 11111111111111111111111111111111
Текущий результат не тот, что мы ожидали!
Оказывается, Java обрабатываетbyte
,short
,char
перед началом смены будетпревратить его вint
введите, а затем выполните операцию! В частности, при использовании этих трех<<=
,>>=
и>>>=
, фактически получаем сдвинутую паруint
Результат после усечения младшего разряда! Проверьте изменения примера:
public static void main(String[] args) {
byte b = -1;
System.out.println("Before >> , b's value is " + b);
System.out.println("b's binary string is " + Integer.toBinaryString(b));
System.out.println("After >> , b's value is " + (b >>> 6));
System.out.println("b's binary string is " + Integer.toBinaryString(b >>> 6));
}
В этом примере нет>>>=
правильноb
переназначение, но непосредственноb >>> 6
вывод (обратите внимание,b >>> 6
Результатint
type), вывод которого выглядит следующим образом:
Before >> , b's value is -1
b's binary string is 11111111111111111111111111111111
After >> , b's value is 67108863
b's binary string is 11111111111111111111111111
Следовательно, реальный процесс работы в первом примере должен быть таким:
заshort
иchar
Принцип работы сдвига тот же, и читатель может провести экспериментальную проверку самостоятельно.
Что произойдет, если количество сдвинутых битов превысит количество битов, занимаемых значением?
Во всех приведенных до сих пор примерах число сдвинутых битов находится в пределах числа битов, занимаемых значением, например, дляint
Ни один из сдвигов типа не превышает 32. тогда, если правильноint
Что происходит, когда тип сдвинут более чем на 32 бита? И посмотрите следующий пример:
public static void main(String[] args) {
int i4 = -1;
System.out.println("Before >>> , i4's value is " + i4);
System.out.println("i4's binary string is " + Integer.toBinaryString(i4));
System.out.println("After >>> 31 , i4's value is " + (i4 >>> 31));
System.out.println("i4's binary string is " + Integer.toBinaryString(i4 >>> 31));
System.out.println("After >>> 32 , i4's value is " + (i4 >>> 32));
System.out.println("i4's binary string is " + Integer.toBinaryString(i4 >>> 32));
System.out.println("After >>> 33 , i4's value is " + (i4 >>> 33));
System.out.println("i4's binary string is " + Integer.toBinaryString(i4 >>> 33));
}
По описанному выше принципу дляi4 >>> 31
Мы легко можем заключить, что1
.
Так,i4 >>> 32
результат будет0
?
NO! Java правый операнд оператора сдвигаrhs
Существуют специальные методы леченияint
Типа, взять только его младшие 5 бит, то есть взятьrhs % 32
Результат; для длинного типа берутся только младшие 6 бит, то есть берутсяrhs % 64
результат. Следовательно, дляi4 >>> 32
,в действительностиi4 >>> (32 % 32)
, этоi4 >>> 0
, результат все равно-1
.
Точно так же дляi4 >>> 33
Эквивалентноi4 >>> 1
, результат2147483647
.
Таким образом, вывод приведенного выше примера выглядит следующим образом:
Before >>> , i4's value is -1
i4's binary string is 11111111111111111111111111111111
After >>> 31 , i4's value is 1
i4's binary string is 1
After >>> 32 , i4's value is -1
i4's binary string is 11111111111111111111111111111111
After >>> 33 , i4's value is 2147483647
i4's binary string is 1111111111111111111111111111111
заlong
То же самое относится и к типам, и читатели могут сами провести экспериментальную проверку.
Суммировать
Хотя оператор сдвига является одним из самых основных операторов в Java, если вы не совсем понимаете детали, легко сделать ошибку, если вы не будете осторожны. Типы, которые на самом деле поддерживаются операторами сдвига, являются толькоint
иlong
, компилятор исправляетshort
,byte
,char
тип преобразуется вint
введите снова. Наиболее распространенное использование оператора сдвига в исходном коде JDK — трактовать его как умножение.*
или кроме/
Использование оператора: сдвиг целого числа влево на единицу, что эквивалентно умножению на 2; Причина в том, что по сравнению с использованием*
и/
, используемый в коде Java<<
и>>
Преобразованный скрипт будет работать более эффективно.