编程语言
首页 > 编程语言> > Integer.toBinaryString()源码探究

Integer.toBinaryString()源码探究

作者:互联网

在Integer类中有静态方法toBinaryString(int i)方法,此方法返回int变量的二进制表示的字符串。
同理,Integer类中也提供了toHexString(int i)方法和toOctalString(int i)方法来分别返回int变量的16进制表示和8进制便是字符串。
三个方法的源码分别为:

public static String toBinaryString(int i) {
        return toUnsignedString0(i, 1);
    }
public static String toOctalString(int i) {
        return toUnsignedString0(i, 3);
    }
 public static String toHexString(int i) {
        return toUnsignedString0(i, 4);
    }

从上面的三段代码可以看出他们都使用了同一个函数toUnsignedString0(int val,int shift),源代码如下:

 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);
        if (COMPACT_STRINGS) {
            byte[] buf = new byte[chars];
            formatUnsignedInt(val, shift, buf, 0, chars);
            return new String(buf, LATIN1);
        } else {
            byte[] buf = new byte[chars * 2];
            formatUnsignedIntUTF16(val, shift, buf, 0, chars);
            return new String(buf, UTF16);
        }
    }

代码主要流程是两步:第一步,计算出用于表示二/八/十六进制的字符数组的长度;第二步,使用formatUnsignedInt方法填充字符数组,得到所需进制表示的字符串并返回。
第一步中字符数组长度的计算是通过numberOfLeadingZeros方法计算int变量的计算机二进制表示的高位连续0位的数量,进而获得最高非0位到最低位的长度,也就是需要表示的位数。【比如整数18在计算机中的二进制存储位0000,0000,0000,0000,0000,0000,0001,0010,那么需要表示的部分便是1,0010,前面的28位均为0,不用表示。】
如何获得变量二进制表示中高位连续的0的个数呢?一般能给出的解决方法是通过判断高位是否为0,然后移位重复判断,记录连续的0的个数。如下:

public static int numberOfLeadingZeros0(int i) {
		if (i==0) {
			return 32;
		}
		int n=0;
		int mask=0x80000000;
		int j=i&mask;
		System.out.println(j);
		while(j==0) {
			n++;
			i<<=1;
			j=i&mask;
			System.out.println(j);
		}
		return n;
	}

这个方法的平均时间复杂度为o(k),k为变量i的位数,如果i为int,k就是32.看起来好像可以接受,但这种很基本的库函数会经过很多次的调用,需要进一度的优化。下面是java源码中的写法(简易的二分法):

public static int numberOfLeadingZeros0(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;
	}

对源码的看法:

让我们回到toUnsignedString0函数的源码,我们刚刚解释了如何获取到二进制中前面连续0的个数,而Integer.SIZE是int的位数,也就是32,求得mag为val值除去前面连续的零后所需要的位数,chars为新建字符数组的长度,其中shift的值代表进制,shift=1代表二进制,最后用formatunsignedInt对字符数组进行赋值。
formatUnsignedInt(int val,int shift,char[] buf, int offset, int len)函数

static void formatUnsignedInt(int val, int shift, byte[] buf, int offset, int len) {
        int charPos = offset + len;
        int radix = 1 << shift;
        int mask = radix - 1;
        do {
            buf[--charPos] = (byte)Integer.digits[val & mask];
            val >>>= shift;
        } while (charPos > offset);
    }
//int到char转化的方式,它是通过一个char[]实现从int到char的转化
static final char[] digits = {
        '0' , '1' , '2' , '3' , '4' , '5' ,
        '6' , '7' , '8' , '9' , 'a' , 'b' ,
        'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
    };

通过val&mask和val的右移位进行赋值,最后逐步返回这个二进制字符串数组。【个人认为完全可以通过char c=(char)((val&mask)+48)实现】

标签:val,int,shift,char,源码,static,toBinaryString,数组,Integer
来源: https://blog.csdn.net/weixin_43872459/article/details/86560753