这两个方法实现的思想是循环左移和循环右移。首先来理解这两个概念

循环移位就是把数值变成二进制,然后循环移动的过程;换句话说,循环移位就是将移出的低位放到该数的高位(循环右移)或把移出的高位放到该数的低位(循环左移),左移,和右移动都是对整数进行的操作,在Win32控制台应用程序中,整形占4Byte节32bit。

循环左移的过程:

循环左移的过程可以分为3步:

1. 将x左端的n位先移动到y的低n位中,x>>(32-n);

2. 将x左移n位,其右面低位补0,x<3. 进行按位或运算(x >> (32 - n) | (x << n));

看到 x >> (32-n)的如果觉得与代码写得不符,先憋着,往后看。

循环右移的过程:

循环右移的过程可以分为3步:

1. 将x的左端的低n位先移动到y的高n位中x<

2. 将x右移n位,其左面高n位补0x>>n;

3.进行按位或操作(x << (32 - n) | (x >> n));

上代码:

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)位,或运算之后就是结果啦。

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

public static long reverse(long i) {

// HD, Figure 7-1

i = (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;

}

先来看一个例子:

二进制数反转

这里有一串数字:87654321,希望实现对他反转后的结果是:12345678。此时我的实现思路是:

第一次反转,一位一位的进行反转: 78563412    在这里我们实现了每两位的排序

第二次反转,既然每两位的已经排序了,那么现在就两位两位的进行反转:这时就变为了: 56781234 这里实现了每4位的排序

第三次反转:既然每四位已经排好序了,这时我们就每四位四位的进行反转:这时就变为了: 12345678。

这时就实现了排序的完成。

从整体上说,这个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-1

i = (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-14

i = 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位即可。

文章来源:

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

循环移位:循环左移和循环右移

rotate java 参数_java rotateLeft()和rotateRight()方法相关推荐

  1. java程序设计_JAVA基础程序设计之方法

    1 基本概念 Java 方法是语句的集合,它们在一起执行一个功能. l 方法是解决一类问题的步骤的有序组合 l 方法包含于类或对象中 l 方法在程序中被创建,在其他地方被引用 1.1 方法的定义 一般 ...

  2. 替代java参数_java – 使用Void作为可选参数的更好的替代方法

    我有一个接口指定方法,它采用泛型类型作为输入,用于创建URL. interface UrlGenerator { String prepareUrl( T input ); } 有一个实现不需要参数. ...

  3. c java 传参数_Java和C的方法参数传递方式的比较

    java中的形参是复制实参的一份拷贝(对于引用类型则是复制引用的拷贝,在栈中的拷贝),所以在函数中改变形参是无法改变实参的值的,改变引用只是将形参所代表的引用指向另外的新的对象,而实参的引用还指向原来 ...

  4. java ready()_Java.io.BufferedReader.ready()方法实例

    全屏 java.io.BufferedReader.ready()方法通知流是否已准备好被读取.一个缓冲字符流是只准备当缓冲区不为空,或者底层流已准备就绪. 声明 以下是java.io.Buffere ...

  5. java nextbyte()_java.util.Scanner.hasNextByte()方法实例

    全屏 java.util.Scanner.hasNextByte()如果在此scanner输入信息中的下一个标记可以使用nextByte()方法被解释为一个字节值的默认基数,方法返回true.scan ...

  6. linux shell java 参数_java调用linux中的shell脚本传递参数并返回执行结果

    [刚接触 linux下的开发,最近遇到java调用shell脚本的问题,找到一个比较适合菜鸟级的方法,转述如下: 在需要运行的SHELL脚本第一行添加 #!/bin/sh然后在终端运行  chmod ...

  7. future java 超时_Java使用Future设置方法超时

    1.Future 它提供了方法来检查是否计算已经完成,还是正在计算而处于等待状态,并且也提供了获取计算结果 方法.当计算完成后,只能通过get方法来获取执行结果,必要的话该方法会阻塞.通过cancel ...

  8. java 耗时_Java使用简单的方法计算代码耗时

    前言:该博客主要是记录自己学习的过程,方便以后查看,当然也希望能够帮到大家. 说明 在我们的实际开发中,多多少少会遇到统计一段代码片段的耗时的情况,下面分享本人常用的方法. 第一步,在pom.xml加 ...

  9. java 复合_Java复合语句的使用方法详解

    与 C 语言及其他语言相同, Java 语言的复合语句是以整个块区为单位的语句,所以又称为块语句.下面我们来看看有关复合语句的使用方法和实例. 复合语句由开括号"{"开始,闭括号& ...

最新文章

  1. 3星|《财经》年刊:各行业专家学者的现状分析与短期预测
  2. 广东移动节能绿色数据中心掀起“能耗革命”
  3. c# winform 打包(带数据库安装)
  4. python mock_Python中使用mock.Mock()进行mock测试
  5. css里的positioning scheme, 即position property
  6. Servlet的运行方式
  7. mysql 条件分析_数据分析之mysql
  8. js 把字符串格式化成时间
  9. Windows 7下ghost Windows XP不能启动的解决方法
  10. tinymce中粘贴word文本时保留格式
  11. 【图像增强】基于matlab暗通道先验图像去雾【含Matlab源码 1367期】
  12. hutool常用方法,工具类
  13. hive整和mysql外表_hive中的外表EXTERNAL TABLE
  14. 简单典型二阶系统_威海召开迎接全国医疗保障系统行风建设专项评价工作会议...
  15. ctcpejmu单词_微生物英文单词
  16. AltiumDesigner中如何将原理图导成黑白色图
  17. 加入NLP交流群和求职群
  18. 人体疲劳程度检测,生理信号处理
  19. 毕业设计 基于JavaWeb的奖学金评定管理系统
  20. 视频编码:H.264编码

热门文章

  1. 16 .net core http请求
  2. 一名IT从业者的英语口语能力成长路径
  3. 上架各个应用商店所需资料
  4. CentOS-7-x86_64 iso镜像的安装(Linux操作系统)
  5. 鸡尾酒排序算法(java)
  6. “经济逆风”袭来 十年轮回IT行业再入下行周期(草根网)
  7. 基于android的网络选课系统
  8. 学习笔记:上网认证6 批量wifi 使用 freeradius 实现统一中央认证
  9. Cadence-元器件PCB封装绘制-Allegro PCB Designer使用方式
  10. Android 12源码编译报错:FAILED: out/soong/build.ninja