JDK中有灵性的代码(一)

月亮不会奔你而来,星星也不会,显然我也不会

1. Integer.highestOneBit

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

目的

可以认为这个方法取得是最高位的权重,比如,3的二进制是000…11,调用该方法后返回的就是000…10,也就是十进制的2,如果参数是-1,那最后的返回值就是Integer.MIN_VALUE

代码思路

以二进制00101001为例,两个步骤:

  1. 将最高位及其之后的位全部填充1,得到00111111
  2. 无符号右移一位后得00011111,然后00111111-00011111=00100000,也就是最终结果

关键步骤是第一步,我们看代码具体是怎么操作的。首先要明确一点,因为一直是做的操作,所以当一位值为1的时候,他就会一直是1

我们依然拿8位二进制举例,取极端情况10000000

  1. i |= (i >> 1)可以认为是将某一位的1延伸到其后面一位,比如我们就得到了11000000
  2. i |= (i >> 2)可以认为是将某一位的1延伸到其后面两位,再配合第一步,就相当于一共覆盖了4位,比如我们现在就得到了11110000
  3. 依法炮制,我们这次得到了11111111
  4. 无符号右移,高位补零,得到011111111
  5. 易得结果

也正因为Java的int是32位的,所以它的最后一次是右移16位


2. Integer.lowestOneBit

public static int lowestOneBit(int i) {
    return i & -i;
}

目的

highestOneBit相对应,这里取得是最低位

代码思路

首先,Java的int类型是补码表示的,补码取复数的操作是取反之后加1,对于数字x中最低位的1,其右侧全部为0,取反之后,右侧全为1,其本身变为0,而其左侧则变为原来值的取反,显然取and之后就得到了该位

比如8位的二进制数字10101000

  1. 取反得01010111
  2. 加1之后得01011000(观察到其最低位1的右侧全为0,而左侧变成原来值的取反)
  3. 取and,得00001000

3. Integer.numberOfLeadingZeros

public static int numberOfLeadingZeros(int i) {
    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;
}

目的
求前导零的个数

代码思路

  1. 判断前16位是否为零,如果是,记录下来,左移16位避免重复计算
  2. 判断前8位是否为零,如果是记录下来,左移8位避免重复计算
  3. 巴拉巴拉巴拉以此类推

注意,n -= i >>> 31有什么意义?因为代码中最后之判断了前2位是否为零,并没有包含对第一位是否为零的判断,从int n = 1中可以看出它是默认第一位是0的,而最后那一句的意义就是在首位为1的情况下把结果做一个矫正。

下面这段代码其实更符合逻辑的一致性,更容易理解

static int numberOfLeadingZeros(byte i) {
    if(i==0)return 8;
    int ret = 0;
    if(i>>>4 == 0){ret+=4;i<<=4;}
    if(i>>>6 == 0){ret+=2;i<<=2;}
    if(i>>>7 == 0){ret+=1;}
    return ret;
}

4. 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的个数

代码思路

看过了求前导零的方法,这个方法就非常容易理解了,但是看起来这两个方法的风格有点不太一样,而且这个return有点酷炫

这里我按照求前导零代码的风格写了一个,结果是一样的

static int numberOfTrailingZeros(int i) {
    if(i==0)return 32;
    int ret = 31;
    if(i<<16 != 0){ret-=16;i <<=16;}
    if(i<<8 != 0){ret-=8; i<<= 8;}
    if(i<<4 != 0){ret-=4;i<<=4;}
    if(i<<2!=0){ret-=2;i<<=2;}
    if(i<<1!=0){ret--;}
    return ret;
}

5. Integer.toString(int i, int radix)

目的

将一个int数值转化成对应进制的字符串

代码思路

代码思路其实很简单,就是不断取余+除,只是人家写的看起来比较厉害

public static String toString(int i, int radix) {
	// 进制范围2-32,非法参数就取10
    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
        radix = 10;

    /* Use the faster version */
    if (radix == 10) {
        return toString(i);
    }
	// 32位int加一个符号位
    char buf[] = new char[33];
    boolean negative = (i < 0);
    int charPos = 32;

    if (!negative) {
        i = -i;
    }
		
	// 不断取余,填充最低位
    while (i <= -radix) {
        buf[charPos--] = digits[-(i % radix)];
        i = i / radix;
    }
    // 处理小于进制数值的最高位
    buf[charPos] = digits[-i];

	// 添加符号位
    if (negative) {
        buf[--charPos] = '-';
    }

    return new String(buf, charPos, (33 - charPos));
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值