Integer中的相关函数实现源码分析

原创 2016年08月31日 15:43:30

1. Integer.bitCount

    public static int bitCount(int i) {
        // HD, Figure 5-2
        i = i - ( (i >>> 1) & 0x55555555  );
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
        i = (i + (i >>> 4)) & 0x0f0f0f0f;
        i = i + (i >>> 8);
        i = i + (i >>> 16);
        return i & 0x3f;
    }

分析:开始死活都没看懂是什么原理,后来网上搜了一顿,大都是从同一篇文章转过去的,说的也不是很清楚,后来翻了Hacker’s Delight,才算是弄明白了。过程推导如下:

        为了快速的求整数中二进制表示中1的位数,采用了二分法,思想如下:首先设置每个2位字段为原来的两个单个位的和(即开始每两个bit位为一组,组内求和),将结果放入相应的中间数中,如何对中间数重新分组,每4位一组,组内求和,以此类推。
        实例如下:
这里写图片描述

        这个思想和归并排序类似,要统计32个bit位的和,先分成两组,每组16bit,分别求和,然后将和加起来。而对于16bit数据的求和,转化合成8bit的求和,以次类推。
        算法的复杂度为:O(logN) (因为归并结果的复杂度为O(1),复杂度由主定理得出)

        上面的思想用代码表示就是:

        i= (i&0x55555555) +  ((i>>>1)&0x55555555 ); 
        i= (i&0x33333333) +  ((i>>>2)&0x33333333 );
        i= (i&0x0F0F0F0F) +  ((i>>>4)&0x0F0F0F0F );
        i= (i&0x00FF00FF) +  ((i>>>8)&0x00FF00FF );
        i= (i&0x0000FFFF) +  ((i>>>16)&0x0000FFFF );

        return i ;

        这种样子就比较好理解了。但是上面的代码还可以优化(显然,不然JDK里就是用这个样子的了)。
        优化思路:
最后一个与没有必要?
并且当资格字段的和不会产生到邻近字段的进位时,相关的与也是何以省略的(int型数据最多32个1,相邻4个bit加起来的和最多为4,相邻8个bit加起来的和最多为8,相邻16个bit加起来的和最多为16,即步骤中的i&0x0F0F0F0F=i, i&0x00FF00FF=i,i&0x0000FFFF=i)。
第一行的代码可以通过下面的公式简化:(原理还没看懂):
pop(x)=x- x/2 -x/4 - ….. -x/(2^31);
        通过上面的化简,就变成了JDK中的样子了。

2. Integer.highestOneBit

    public static int highestOneBit(int i) {
        // HD, Figure 3-1
        i |= (i >>  1);
        i |= (i >>  2);
        i |= (i >>  4);
        i |= (i >>  8);
        i |= (i >> 16);
        return i - (i >>> 1);
    }

        原理分析:这个不好解释,举例说明吧,假如求X的最高有效位,假如最高位为第m位,则从低到高第m位一定为1.之后的m-1位可为0,可为1。这个求highestOneBit的思想是先将X变成Y,其中Y中的右边m位全为1.则X和Y的最高位相同,且Y+1=2^m,则结果值就是将Y的底m-1位清零,返回。
        如X=1010,Y=1111。
Y-Y>>>1=1111-0111=1000

3. Integer.lowestOneBit

    public static int lowestOneBit(int i) {
        // HD, Section 2-1
        return i & -i;
    }

        这个就不解释了,比较简单。(因为-i和i的最后一位1位置是相同的)
4. Integer.numberOfLeadingZeros

    public static int numberOfLeadingZeros(int i) {
        // HD, Figure 5-6
        if (i == 0)
            return 32;
        int n = 1;
        if (i >>> 16 == 0) { n += 16; i <<= 16; }
        if (i >>> 24 == 0) { n +=  8; i <<=  8; }
        if (i >>> 28 == 0) { n +=  4; i <<=  4; }
        if (i >>> 30 == 0) { n +=  2; i <<=  2; }
        n -= i >>> 31;
        return n;
    }

        原理分析:就是一步一步的数前面0的个数,但是这个算法的一个优点就是比较快的收缩了。如果i高16位为0,那就count+16,在加上低16位中高位中0的个数,为了算低16位中前导0的个数,将低16位放到前面。在低16位的计算中,如果高8位为0,则结果加8,同时将后8位放到最前,以此类推。

        最后一条语句n -= i >>> 31,是下面两条语句的优化

int n = 0;
if (i >>> 31 == 0) //16+8+4+2
            { n +=  1;  }

5. Integer.numberOfTrailingZeros

    public static int numberOfTrailingZeros(int i) {
        // HD, Figure 5-14
        int y;
        if (i == 0) return 32;
        int n = 31;
        y = i <<16; if (y != 0) { n = n -16; i = y; }
        y = i << 8; if (y != 0) { n = n - 8; i = y; }
        y = i << 4; if (y != 0) { n = n - 4; i = y; }
        y = i << 2; if (y != 0) { n = n - 2; i = y; }
        return n - ((i << 1) >>> 31);
    }

        原理分析:前导0的逆运算

6.Integer.toBinaryString()

    public static String toBinaryString(int i) {
        return toUnsignedString0(i, 1);
    }

    private static String toUnsignedString0(int val, int shift) {
        // assert shift > 0 && shift <=5 : "Illegal shift value";
        int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
        int chars = Math.max(((mag + (shift - 1)) / shift), 1);
        char[] buf = new char[chars];

        formatUnsignedInt(val, shift, buf, 0, chars);

        // Use special constructor which takes over "buf".
        return new String(buf, true);
    }

        需要注意的是转化成的string的位数为32-Integer.numberOfLeadingZeros();
7. 总结
        几个二进制运算的技巧:

  • 将最右侧的1改为0:x&(x-1)
  • 检查无符号整数是否为2的幂:x&(x-1) ==0
  • 检查无符号整数是否为2^n -1的形式:x&(x+1) ==0
  • 求最右侧的1:x&(-x)
  • 求最右侧的0:-x&(x+1)
  • 构造识别后缀0的掩码,如果x=0,则生成所有位都为1的字(如0101000=>0000111)
    ~x &(x-1) 或者 ~(x| -x)或者 (x& -x)-1
版权声明:转载 请标注 出处

Integer源码解析

这篇博客我来整理下以Integer为例整理下包装类的源码。首先来看一段代码: public class LinkinPark { public static void main(String[]...
  • u011794238
  • u011794238
  • 2016年05月22日 16:55
  • 770

Integer.bitCount(int i)原理

/** * Returns the number of one-bits in the two's complement binary * representation of th...
  • u012891380
  • u012891380
  • 2014年09月10日 19:24
  • 2584

Java源码 Integer.bitCount实现过程

public static int bitCount(int i) { // HD, Figure 5-2 i = i - ((i >>> 1) & 0x55555555); ...
  • u011042188
  • u011042188
  • 2016年12月18日 02:49
  • 2015

Integer.bitCount函数解释

public static int bitCount(int i) { // HD, Figure 5-2 i = i - ((i >>> 1) & 0x5555555...
  • zhouzipeng000
  • zhouzipeng000
  • 2017年02月23日 17:33
  • 636

Java基础之---java Integer类内置方法浅析(1)

Java 中Integer是int的派生类型,内置了一些常用的对int类型数据的处理方法 下面对其简单的分析。  1, String toBinaryString(int i) 给定一个int...
  • John_desheng
  • John_desheng
  • 2016年10月27日 10:19
  • 368

Integer.bitCount()理解

本文讨论内容基于Jdk1.7  bitCount实现的功能是计算一个(byte,short,char,int统一按照int方法计算)int,long类型的数值在二进制下“1”的数量。  网上关于此...
  • Qingzhaoi
  • Qingzhaoi
  • 2017年09月12日 18:15
  • 36

Integer源码详解

尊重原创,转载请标明出处 http://blog.csdn.net/abcdef314159 对于Integer这个类估计大家也都非常熟悉了,以前看过他的源码,但也只是粗略的看了一下,最近有时间认真的...
  • abcdef314159
  • abcdef314159
  • 2017年08月10日 18:14
  • 508

Java关于Integer面试题

package day13_Integer; /* * JDK5的新特性: * 自动装箱:把基本类型转为包装类类型 * 自动拆箱:把包装类类型转为基本类型 * * Integer面试题...
  • xiakexiaohu
  • xiakexiaohu
  • 2017年06月05日 17:39
  • 319

Integer的自动拆箱和自动装箱的陷阱(整型数-128到127的值比较问题)

原文地址:http://blog.csdn.net/ma451152002/article/details/9076793 Integer的自动拆装箱的陷阱(整型数-128到127的值比较问题)...
  • tanga842428
  • tanga842428
  • 2016年10月11日 17:08
  • 1190

Integer.bitCount(int i)求二进制数中1的个数

解法一: 对于一个正整数如果是偶数,该数的二进制数的最后一位是 0 ,反之若是奇数,则该数的二进制数的最后一位是 1 。因此,可以考虑利用位移、判断奇偶来实现。 Java代码   publi...
  • Elmo66
  • Elmo66
  • 2017年12月20日 21:56
  • 67
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Integer中的相关函数实现源码分析
举报原因:
原因补充:

(最多只允许输入30个字)