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.bitCount函数解释

public static int bitCount(int i) { // HD, Figure 5-2 i = i - ((i >>> 1) & 0x5555555...

Integer.highestOneBit、 Integer.bitCount 实现解析

求一个二进制数中 bit=1的个数

Java源码 Integer.bitCount实现过程

public static int bitCount(int i) { // HD, Figure 5-2 i = i - ((i >>> 1) & 0x55555555); ...

Integer.bitCount(int i)原理

/** * Returns the number of one-bits in the two's complement binary * representation of th...

libevent源码分析(6)--2.1.8--创建和释放libevent句柄event_base的相关函数

一、event_base_new函数:创建默认的event_base ** * Create and return a new event_base to use with the rest of ...

Libevent源码分析-----与event相关的一些函数和操作

Libevent提供了一些与event相关的操作函数。本文就重点讲一下这方面的源代码。   在Libevent中,无论是event还是event_base,都是使用指针而不会使用变量。实际上,如果...

java.lang.Integer源码分析

1>compareTo(Integer anotherInteger) : 在数字上比较两个 Integer 对象。 返回有三个结果: :相等返回0 :first > second 返回1 :...

Integer源码分析

参考:http://www.hollischuang.com/archives/1058类定义public final class Integer extends Number implements ...

63_常用类_包装类_Integer_Number_JDK源码分析

基本数据类型的包装类包装类基本知识 JAVA并不是纯面向对象的语言。Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的。但是我们在实际使用中经常需要将基本数据转化...

【实践】java.lang.Integer源码分析 -- parseInt

parseInt其实就是将String字符串每一位取出来以后重新计算转换为其表示的int数值...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Integer中的相关函数实现源码分析
举报原因:
原因补充:

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