关闭

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

标签: 源码Integer
423人阅读 评论(0) 收藏 举报
分类:

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
1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:48647次
    • 积分:1181
    • 等级:
    • 排名:千里之外
    • 原创:57篇
    • 转载:28篇
    • 译文:7篇
    • 评论:6条
    最新评论