2019独角兽企业重金招聘Python工程师标准>>>

文章看过后感觉受益匪浅,所以留下了以备温故:http://www.congmo.net/blog/2012/03/11/Long-ByteShifting/

本篇主要讲述位移运算在Long中所扮演的重要角色,有些出神入化的我根本无法理解,但是我一直秉承着无论对错都要记录思考的过程的宗旨写每一篇文章。这一篇也不例外,读Long这个类确实需要比较广泛的知识面,我也是一边在OSChina和stackoverflow上提问,一边慢慢的钻研,难免会存在偏差。

先来看个简单的。

public static int signum(long i) {// HD, Section 2-7return (int) ((i >> 63) | (-i >>> 63));
}

这个函数作用就是返回参数i的符号,如果返回-1则是负数,如果返回0则是0,如果返回1则是正数。算法就是(int) ((i >> 63) | (-i >>> 63)),如果是正数的话i有符号右移63位后为0,-i无符号右移63位之后结果为1,或操作之后结果就是1.如果i为负数,那么有符号右移63位后就变成了1,然后-i无符号右移63位后就只剩下符号位,最后做或(|)操作结果就是-1. 如果参数i为0,那么移位后结果就是0.

System.out.println(Long.signum(100L));
System.out.println(Long.signum(0L));
System.out.println(Long.signum(-100L));
 
输出结果:10-1

接着是一个很少用到,但是实现方式不错的两个方法,循环左移和循环右移方法。

public static long rotateLeft(long i, int distance) {return (i << distance) | (i >>> -distance);
}
public static long rotateRight(long i, int distance) {return (i >>> distance) | (i << -distance);
}

实现的代码量可以说已经精简到最少了,有一点要注意的是,循环移位时,参数distance可以接受负数,当distance为负数时,这个等式是成立的,rotateLeft(i, distance) = rotateRight(i, -distance)。这个方法中有两点值得借鉴的,第一从整体上讲循环移位的实现方式;第二是distance与-distance的巧妙运用。

就拿循环左移先来说说第二点吧,前置条件,我们首先假设distance大于0,起先我是很不理解i >>> -distance的,后来在stackoverflow上发问,有人给出了解释,在移位的时候,如果distance小于0,会根据被移位数的长度进行转换。就比如说这里我们对long进行移位,那么-distance就会被转换成(64 + distance)(注,这里的distance是小于0的)。这样的话,如果distance大于0时,(i << distance) | (i >>> -distance);就会被转化成(i << distance) | (i >>> 64 + distance);

清楚了第二点,那么第一点也就不难理解了。用一幅图来解释循环左移。

在distance大于0的前提下,先左移distance位,然后再右移64-distance,最终用或运算相加,就是循环移位的结果。图中为了省事儿用了8位做了个演示,先左移3位,然后右移(8-3)位,或运算之后就是结果啦。关于-distance在stackoverflow上的提问在这里。

下面是个更给力的方法-reverse(long i),可以说就是高效率的化身。

public static long reverse(long i) {// HD, Figure 7-1i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;i = (i << 48) | ((i & 0xffff0000L) << 16) |((i >>> 16) & 0xffff0000L) | (i >>> 48);return i;
}

从整体上说,这个reverse方法集移位与二分算法于一身,堪称经典。 第一步以单位为单位,奇偶位交换 第二步以两位为单位,完成前后两位的交换。 第三步以四位为单位,完成前后四位的交换。 第四步以八位为单位,完成前后八位的交换。 最后一步没有按常理继续二分,而是通过一个转换一步就完成了以16和32位为单位的交换。进而结束了整个64位的反转。

现在一步一步剖析都是如何实现的。

i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;

16进制的5为0101,或操作前半部分首先取出i的所有奇数位,然后整体左移一位,这样实现i的奇数位左移一位变成偶数位;或操作后半部分先右移,即将偶数位右移变成奇数位,然后再取出奇数位。这样就完成了64位中奇数位与偶数位的交换。

i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;

这句同样是实现交换,只不过3对应的16进制为0011,即本次交换以2个字节为单位,交换完成了4个字节的反转。 Liquid error: Flag value is invalid: -O ”” 直到这行代码,实现了以字节为单位的反转,最后仅仅使用一行代码就实现了一两个字节和四个字节为单位的反转。 为了方便画图,现在对操作进行编号,另外从以字节为单位交换开始,之前的细节忽略。

(i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
(i << 48)
(i & 0xffff0000L) << 16)
(i >>> 16) & 0xffff0000L)
(i >>> 48);

这幅图描述每个编号代码执行之后64位的变化。

不多做解释,由于这个reverse的最后一行不是按常理”出牌”,所以我使用纯粹的二分法来实现reverse。

public static long reverse(long i) {// HD, Figure 7-1i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;i = (i & 0x0000ffff0000ffffL) << 16 | (i >>> 16) & 0x0000ffff0000ffffL;i = (i & 0x00000000ffffffffL) << 32 | (i >>> 32) & 0x00000000ffffffffL;return i;
}

至于为什么要采用那种方式,而不是用”纯粹”的二分法,在stackoverflow上有人提到,可能是因为前一种实现方式需要9个操作,而后一种实现方式需要10个操作。具体是出于怎样的目的可能只有作者才知道。关于reverse我在stackoverflow上的提问在这里。

最后看一个方法。

public static int bitCount(long i) {// HD, Figure 5-14i = i - ((i >>> 1) & 0x5555555555555555L);i = (i & 0x3333333333333333L) + ((i >>> 2) & 0x3333333333333333L);i = (i + (i >>> 4)) & 0x0f0f0f0f0f0f0f0fL;i = i + (i >>> 8);i = i + (i >>> 16);i = i + (i >>> 32);return (int)i & 0x7f;
}

这个方法是返回一个long类型的数字对应的二进制中1的个数,其实google上有很多种,这里采用的叫平行算法实现的,算法如下图。

但是这个方法的第一行又采取了一个特别的方式实现i中奇数位+偶数位,我有点儿没想明白。总体上就是像图中所示那样,相邻的两位相加的结果再相邻的四位相加,最后得到二进制中1的个数。 还有一点值得提一下,就是最后一行与上7f,因为long类型,1的个数最多也不会超过64个,所以只取最后7位即可。

转载于:https://my.oschina.net/u/3647620/blog/1552442

java java.lang.Long详解之三 大显神通的位移运算相关推荐

  1. Java单元测试之JUnit4详解

    2019独角兽企业重金招聘Python工程师标准>>> Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @B ...

  2. Java编程配置思路详解

    Java编程配置思路详解 SpringBoot虽然提供了很多优秀的starter帮助我们快速开发,可实际生产环境的特殊性,我们依然需要对默认整合配置做自定义操作,提高程序的可控性,虽然你配的不一定比官 ...

  3. Java之toString()方法详解

    Java之toString()方法详解 Java中 toString()方法在Object类中和Intent类中都有定义,作用类似,但显示形式有点区别 一.Object类中toString()方法 t ...

  4. Java基础学习总结(24)——Java单元测试之JUnit4详解

    Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @BeforeClass 全局只会执行一次,而且是第一个运行 @Before  ...

  5. JAVA元注解@interface详解(@Target,@Documented,@Retention,@Inherited)

    转载自 JAVA元注解@interface详解(@Target,@Documented,@Retention,@Inherited) jdk1.5起开始提供了4个元注解,用来定义自定义注解的注解,它们 ...

  6. Java高并发编程详解系列-Java线程入门

    根据自己学的知识加上从各个网站上收集的资料分享一下关于java高并发编程的知识点.对于代码示例会以Maven工程的形式分享到个人的GitHub上面.   首先介绍一下这个系列的东西是什么,这个系列自己 ...

  7. java反射机制深入详解_Java反射机制深入详解

    原标题:Java反射机制深入详解 一.概念 反射就是把Java的各种成分映射成相应的Java类. Class类的构造方法是private,由JVM创建. 反射是java语言的一个特性,它允程序在运行时 ...

  8. Java引用类型分类以及详解

    Java引用类型分类以及详解 - Java引用类型概述 在JVM之中再好的算法,也敌不过一个好烂的程序员.一个程序要想写好有两点:按照开发标准进行.请写有用代码. 而对于垃圾的产生与回收的处理之中,要 ...

  9. java执行cmd命令详解

    前言 Java应用程序主要是通过Runtime和Process两个类来执行cmd命令. Runtime.exec方法创建本机进程并返回Process子类的实例,该实例可用于控制进程并获取有关它的信息. ...

最新文章

  1. 2022-2028年中国环保设备行业投资分析及前景预测报告
  2. 从消费端到企业端,从设备到数据:物联网市场的爆发式增长
  3. cocos2d-x初探学习笔记(20)--物理引擎box2d(2)
  4. 管控研发部门USB设备
  5. 牛客题霸 [ 验证IP地址] C++题解/答案
  6. 产品经理专业知识50篇(五)-用户成长体系设计方案
  7. 迪杰斯特拉算法dijkstra(可打印最短路径)
  8. mac下nginx安装及与tomcat简单配置
  9. mysql存储过程实现_原来MySQL的存储过程也可以这么玩?
  10. 20190910每日一句 你有勇气直面自己的恐惧吗?
  11. python代理IP的使用
  12. ubuntu18.04安装nvidia显卡驱动的正确方法
  13. 蓝桥杯c语言程序题题库,蓝桥杯c语言试题
  14. 最互联网的定制家居增长新势力,如何三招实现疫情期的逆势增长?
  15. HTML5+JS游戏开发模块----canvas打字游戏
  16. Windows服务优化(整理篇)
  17. JEB2插件教程之一JEB2AutoRenameByTypeInfo.py
  18. 单片机中的浮点数转换成串口可打印格式
  19. 联盛德 HLK-W806 (八): 4线SPI驱动SSD1306/SSD1315 128x64 OLED液晶屏
  20. antd DatePicker 组件 月份和星期显示英文

热门文章

  1. html怎么循环输出_for 循环疑难点
  2. python enumerate_python中enumerate的用法实例解析
  3. JavaSE(五)——修饰符、内部类、匿名内部类
  4. firefox 53支持java_Selenium 2.53不使用Firefox 47
  5. .Net 程序员走向高端必读书单汇总
  6. 对话框中WaitForSingleObject等待线程退出导致程序阻塞的原因及解决
  7. mysql与java连接查询_【java】MySQL数据库之连接查询
  8. 出席全球数字经济大会 第四范式助力打造中国数字经济“北京样板”
  9. Java基础day21
  10. wxWidgets随笔(1)-hello,world