1.原码、反码、补码

关于原码、反码、补码的相关知识作者不打算在这里长篇大论,相关知识已有别的大佬总结很好了,还请老铁自行 Google,不过有篇知乎回答是作者学编程以来见过对相关知识最通俗易懂,生动简洁的解释:对原码、反码、补码最通俗易懂,生动简洁的解释,墙裂建议大家先看完这篇科普文章。在继续讨论之前你要先明白一点:整数在计算机内部都是以补码形式存储的

2.Java 位运算概览

OK 都看到这儿了那我就假定你已经掌握了原码、反码、补码相关知识(虽然上面那段几乎啥也没讲,纯凑字数)

不废话了。

首先,在程序中,位运算符的优先级很低!且位运算是只针对整型(是的这里指所有整型)数据的,无论正负,先搞清楚这两点,我们继续说。

Java 中的位运算共有 与(&)、或(|)、非(~)、异或(^)、左移(<<)、右移(>>)、无符号右移(>>>) 这7种,概览见下表:

符号 含义 详解
& 位与 两个比特位都为 1 时,结果才为 1,否则为 0 (位与操作满足交换律和结合律,甚至分配律)
| 位或 两个比特位都为 0 时,结果才为 0,否则为 1 (位或操作满足交换律和结合律,甚至分配律)
~ 位非 即按位取反,1 变 0,0 变 1
^ 异或 两个比特位相同时(都为 0 或都为 1)为 0,相异为 1(异或操作满足交换律和结合律,甚至分配律。任何整数和自己异或的结果为 0,任何整数与 0 异或值不变)
<< 左移 将所有的二进制位按值向←左移动若干位,左移操作与正负数无关,它只是傻傻地将所有位按值向左移动,高位舍弃,低位补 0
>> 右移 将所有的二进制位按值向右→移动若干位,低位直接舍弃,跟正负无关,而高位补 0 还是补 1 则跟被操作整数的正负相关,正数补 0 ,负数补 1
>>> 无符号右移 将所有的二进制位按值向右→移动若干位,低位直接舍弃,跟正负无关,高位补 0 ,也跟正负无关

这里有几点需要注意:

  1. 这七个位运算符,只有取反(~)运算符是单目运算符,其他均为双目运算符!
  2. 注意上面提到的某几个位运算操作满足交换律和结合律甚至分配律,这很重要!后面你会看到。
  3. 额好吧我目前只想到上面两点需要注意的,别的想到我会更新文章!

还要说一下在 Kotlin 里面,位运算跟 Java 一致也是这七种,只不过运算符号跟 Java 里有点不太一样,Kotlin 里的七种位运算符:

  • and(bits) – 位与 (Java 的 &)
  • or(bits) – 位或 (Java 的 |)
  • inv() – 位非 (Java 的 ~)
  • xor(bits) – 位异或 (Java 的 ^)
  • shl(bits) – 有符号左移 (Java 的 <<)
  • shr(bits) – 有符号右移 (Java 的 >>)
  • ushr(bits) – 无符号右移 (Java 的 >>>)

嗯除了写法其他跟 Java 中一致!

下面我们来看些 Java 中位运算的代码实例。

3.Java 位运算最佳实践(常见操作)

1.位运算基础操作

  1. //对于任意整数 a 和 b,以下表达式的结果均为 true,各位读者朋友可自行验证,真不骗你!
  2. //注意以下几个操作之所以一一在这里特别列出,是因为它们对于位操作而言具有某种普遍意义,也就是说你最好能将它们牢记于心。
  3. //至于它们有怎样的普遍意义,留给读者们自行思考,总之记住实践出真知!
  4. a|0 == a;
  5. a&-1 == a;
  6. a&0 == 0;
  7. a^a == 0;
  8. a^0 == a;
  9. a|~a == -1;
  10. a&~a == 0;
  11. a&a == a;
  12. a|a == a;
  13. a|(a&b) == a;
  14. a&(a|b) == a;
  15. 复制代码

2.位运算进阶操作(部分操作参考了别的大佬博客):

1.判断奇偶

可以很简单归纳出,只需判断一个正整数的二进制补码最后一位是0是1,是0为偶数,是1为奇数

  1. public void isOddOrEven(int n){
  2. if ((n & 1) == 1) {//n是奇数
  3. System.out.println("Odd");
  4. }else {//n是偶数
  5. System.out.println("Even");
  6. }
  7. }
  8. 复制代码
2.省去中间变量交换两整数的值(据说面试常考)
  1. public void swap(){
  2. int a = 1, b = 2;
  3. a ^= b;
  4. b ^= a;//b == 1
  5. a ^= b;//a == 2
  6. System.out.println("a:" + a);//a:2
  7. System.out.println("b:" + b);//b:1
  8. }
  9. 复制代码
3.变换符号,正变负,负变正

只需对待操作数应用取反操作后再加 1 即可

  1. public void negate(){
  2. int a = -10, b = 10;
  3. System.out.println(~a + 1);//10
  4. System.out.println(~b + 1);//-10
  5. }
  6. 复制代码
4.求绝对值,

对于负数可以通过上面变换符号的操作得到绝对值, 正数直接返回即可,因此我们要先判断符号位来得知当前数的正负。

  1. public int abs(int a){
  2. int i = a >> 31;//得到符号位,0 为正数,-1 为负数
  3. return i == 0 ? a : (~a + 1);//符号位为 0 直接返回,否则返回 ~a + 1
  4. }
  5. 复制代码

或者,n>>31 取得n的符号,若n为正数,n>>31等于0,若n为负数,n>>31等于-1 若n为正数 n^0-0 数不变,若n为负数n^-1 需要计算n和-1的补码,异或后再取补码, 结果n变号并且绝对值减1,再减去-1就是绝对值

  1. public int abs(int a){
  2. return a ^ (a >> 31)) - (a >> 31);
  3. }
  4. 复制代码
5. 判断一个数num是不是 2 的幂

如果是2的幂,n一定是100... (也就是二进制位里只有一个是1,且是首位,其余全是0),n-1就是011.... 所以做与运算结果为 0,这里贴下 Kotlin 代码:

  1. fun isPowerOfTwo(num: Int): Boolean {
  2. if (num < 1){
  3. return false
  4. }
  5. return num.and(num - 1) == 0
  6. }
  7. 复制代码
6.判断一个数num是不是 4 的幂

理论上数字4幂的二进制类似于100,10000,1000000,... 这些形式。 不难归纳出如下结论: 4的幂一定是2的。 4的幂和2的幂一样,二进制位里只有一个是1,且是首位。但是,4的幂中的1总是出现在奇数位。 这里贴下 Kotlin 代码(非最佳实现):

  1. fun isPowerOfFour(num: Int): Boolean {
  2. if (num < 1){
  3. return false
  4. }
  5. return (num.and(num - 1) == 0 && Integer.toBinaryString(num).length.and(1) == 1)
  6. }
  7. 复制代码
其他进阶操作
  1. // 7. int 型最大值是什么?
  2. System.out.println((1 << 31) - 1);// 2147483647, 注意运算符优先级,括号不可省略
  3. System.out.println(~(1 << 31));// 2147483647
  4. // 8. int 型最小值是什么?
  5. System.out.println(1 << 31);
  6. System.out.println(1 << -1);
  7. // 9. long 型最大值是什么?
  8. System.out.println(((long)1 << 127) - 1);
  9. // 10. 整数n乘以2是多少?
  10. n << 1;
  11. // 11. 整数n除以2是多少?(负奇数的运算不可用)
  12. n >> 1;
  13. // 12. 乘以2的n次方,例如计算10 * 8(8是2的3次方)
  14. System.out.println(10<<3);
  15. // 13. 除以2的n次方,例如计算16 / 8(8是2的3次方)
  16. System.out.println(16>>3);
  17. // 14. 取两个数的最大值(某些机器上,效率比a>b ? a:b高)
  18. System.out.println(b&((a-b)>>31) | a&(~(a-b)>>31));
  19. // 15. 取两个数的最小值(某些机器上,效率比a>b ? b:a高)
  20. System.out.println(a&((a-b)>>31) | b&(~(a-b)>>31));
  21. // 16. 判断符号是否相同(true 表示 x和y有相同的符号, false表示x,y有相反的符号。)
  22. System.out.println((a ^ b) > 0);
  23. // 17. 计算2的n次方 n > 0
  24. System.out.println(2<<(n-1));
  25. // 18. 求两个整数的平均值
  26. System.out.println((a+b) >> 1);
  27. // 19. 从低位到高位,取n的第m位
  28. int m = 2;
  29. System.out.println((n >> (m-1)) & 1);
  30. // 20. 从低位到高位.将n的第m位置为1,将1左移m-1位找到第m位,
  31. //得000...1...000,n 再和这个数做或运算
  32. System.out.println(n | (1<<(m-1)));
  33. // 21. 从低位到高位,将n的第m位置为0,将1左移m-1位找到第m位,
  34. //取反后变成111...0...1111,n再和这个数做与运算
  35. System.out.println(n & ~(0<<(m-1)));
  36. // 22 暂时木有了,想到再加~
  37. 复制代码

4.Java 位运算趣味应用

1.只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

解法: 解题思路其实很简单,就是上面提到的异或操作满足交换律和结合律,任何整数和自己异或的结果为 0,任何整数与 0 异或其值不变,我直接贴下 Kotlin 代码

  1. fun singleNumber(nums: IntArray): Int {
  2. var result = 0
  3. nums.forEach {
  4. result = result.xor(it)
  5. }
  6. return result
  7. }
  8. 复制代码

2.寻找缺失数字

给定一个包含 0, 1, 2, ..., n 中 n 个数的序列(无序排列),找出 0 .. n 中没有出现在序列中的那个数。

  • 示例 1: 输入: [0,1,3] 输出: 2
  • 示例 2: 输入: [9,6,4,2,3,5,7,0,1] 输出: 8

解法: 解题思路其实很简单,还是上面提到的异或操作满足交换律和结合律,任何整数和自己异或的结果为 0,任何整数与 0 异或其值不变,我直接贴下 Kotlin 代码

  1. fun missingNumber(nums: IntArray): Int {
  2. var sum = nums.size
  3. nums.forEachIndexed { index, i ->
  4. sum = sum.xor(index)
  5. sum = sum.xor(i)
  6. }
  7. return sum
  8. }
  9. 复制代码

3.额好吧暂时只想到两条,想到别的会更新文章加上

未完待续…

4.Java 位运算操作库

我们以 Java 标准库的 Integer 类为例,emmm…Java 标准库里面封装好的所有位操作方法还请大家自行翻阅 Java 标准库文档哈!我在这里只举几个标准库里面容易被大家忽略的有用方法。 主要有三个:

  1. //返回指定int值的二进制补码表示形式中的值为1的位的个数。
  2. Integer.bitCount(int i)
  3. //使用
  4. //17的二进制补码表示是10001,可以看到里面共有两个位值为1
  5. Integer.bitCount(17) == 2//true
  6. 复制代码
  1. //把指定int值的二进制补码除了值为1的最高位之外的所有非最高位值为1的位——置值为0返回。
  2. Integer.highestOneBit(int i)
  3. //使用
  4. //十进制数 36 的二进制补码表示为100100,应用 highestOneBit 操作后返回二进制补码100000,即十进制的32
  5. Integer.highestOneBit(36) == 32//true
  6. 复制代码
  1. //把指定int值的二进制补码除了值为1的最低位之外的所有非最低位值为1的位——置值为0返回。
  2. Integer.lowestOneBit(int i)
  3. //使用
  4. //十进制数 36 的二进制补码表示为100100,应用 lowestOneBit 操作后返回二进制补码100,即十进制的4
  5. Integer.lowestOneBit(36) == 4//true
  6. 复制代码

Java 位运算超全面总结的更多相关文章

  1. Java 位运算2-LeetCode 201 Bitwise AND of Numbers Range

    在Java位运算总结-leetcode题目博文中总结了Java提供的按位运算操作符,今天又碰到LeetCode中一道按位操作的题目 Given a range [m, n] where 0 <= ...

  2. Java位运算总结:位运算用途广泛《转》

    前天几天研究了下JDK的Collection接口,本来准备接着研究Map接口,可是一查看HashMap类源码傻眼咯,到处是位运算实现,所以我觉得还是有必要先补补位运算知识,不然代码看起来有点费力.今天 ...

  3. Java位运算原理及使用讲解

    前言日常开发中位运算不是很常用,但是巧妙的使用位运算可以大量减少运行开销,优化算法.举个例子,翻转操作比较常见,比如初始值为1,操作一次变为0,再操作一次变为1.可能的做法是使用三木运算符,判断原始值 ...

  4. (转)java位运算

    转自:http://aijuans.iteye.com/blog/1850655 Java 位运算(移位.位与.或.异或.非)   public class Test { public static ...

  5. Java位运算总结:位运算用途广泛

    前天几天研究了下JDK的Collection接口,本来准备接着研究Map接口,可是一查看HashMap类源码傻眼咯,到处是位运算实现,所以我觉得还是有必要先补补位运算知识,不然代码看起来有点费力.今天 ...

  6. 我们必须要了解的Java位运算(不仅限于Java)

    本文原创地址为 https://www.cnblogs.com/zh94/p/16195373.html 原创声明:作者:陈咬金. 博客地址:https://www.cnblogs.com/zh94/ ...

  7. Java位运算经典实例

    一 源码.反码.补码 正数的源码.反码.补码相同,例如5:            5的源码:101            5的反码:101            5的补码:101 负数的源码.反码.补 ...

  8. Java 位运算(移位、位与、或、异或、非)

    Java提供的位运算符有:左移( << ).右移( >> ) .无符号右移( >>> ) .位与( & ) .位或( | ).位非( ~ ).位异或( ...

  9. java位运算

    Java的位运算(bitwise operators)直接对整数类型的位进行操作,这些整数类型包括long.int.short.char和 byte,位运算符具体如下表: 运算符 说明 << ...

随机推荐

  1. C#设计模式:访问者模式(Vistor Pattern)

    一,访问者模式是用来封装一些施加于某种数据结构之上的操作.它使得可以在不改变元素本身的前提下增加作用于这些元素的新操作,访问者模式的目的是把操作从数据结构中分离出来. 二,代码 using Syste ...

  2. C++泛型程序设计---算法和提升

    算法和提升 算法:所谓算法就是一个求解问题的过程或公式,即,通过一个有穷的计算序列生成结果. 函数模板就是普通函数的泛化:它能对多种数据类型执行动作,并且能用以参数方式传递来的各种操作实现要执行的工作 ...

  3. 锋利的JS解读——认识JQuery(一)

    一.jQuery的发展 随着javascript的不断发展,延伸出了多种JS程序库,当前比较流行的js库有:1)Prototype  成型较早,从整体上对面向对象的编程思想把握的不是很到位. 2)Do ...

  4. 修改默认select样式

    <style type="text/css"> .select_demo,.select_list { width: 400px; height: 60px; } .s ...

  5. C语言scanf的返回值

    #include <stdio.h> int main(void) { long num; long sum = 0L; int status; printf("Please e ...

  6. console 对象

    JavaScript 原生中默认是没有 Console 对象,这是宿主对象(也就是游览器)提供的内置对象. 用于访问调试控制台,在不同的浏览器里效果可能不同.Console 对象方法:

  7. vue.js-vuex深入浅出

    1:正确的创建目录 2:action.js 异步请求数据 3:index.js 文件声名 4:mutation.js 同步事务 修改state中的值 5:state.js 申明和保存变量或对象 6:如 ...

  8. 你不知道的props和state

    State 与 Props 区别props 是组件对外的接口,state 是组件对内的接口.组件内可以引用其他组件,组件之间的引用形成了一个树状结构(组件树),如果下层组件需要使用上层组件的数据或方法 ...

  9. 39. Combination Sum (Java)

    Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), fin ...

  10. 利用mysql中if函数排序

    格式:IF(Condition,A,B) 意义:当Condition为TRUE时,返回A:当Condition为FALSE时,返回B. 作用:作为条件语句使用. select if(`from_use ...