JDK源码之Integer类—formatUnsignedInt()方法

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK源码之Integer类—formatUnsignedInt()方法相关的知识,希望对你有一定的参考价值。

前言

将十进制整数转换成其他进制的方法是:整数转换为r进制数时,采用除r取余方法,即将十进制整数不断除以r取余数,直到商为0,所得的余数按逆序排列。

图解:

用Java简单来实现这个代码:

public class Test {
 
    public static void main(String[] args) {
        System.out.println(Integer.toString(123456, 16));// 1e240
        System.out.println(toString(123456, 16));// 1e240
        System.out.println(Integer.toString(987654, 23));// 3c40b
        System.out.println(toString(987654, 23));// 3c40b
    }
 
    public static String toString(int i, int radix) {
        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'
        };
        StringBuilder sb = new StringBuilder();
        while (i > 0) {
            sb.append(digits[i % radix]);
            i /= radix;
        }
        return sb.reverse().toString();
    }
}

formatUnsignedInt()方法

formatUnsignedInt()方法将整数val转换成指定进制数后,填充到字符数组buf中。

该方法的源码如下:

    /**
     * Format a long (treated as unsigned) into a character buffer.
     * @param val the unsigned int to format
     * @param shift the log2 of the base to format in (4 for hex, 3 for octal, 1 for binary)
     * @param buf the character buffer to write to
     * @param offset the offset in the destination buffer to start at
     * @param len the number of characters to write
     * @return the lowest character  location used
     */
     static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
        int charPos = len;
        int radix = 1 << shift;
        int mask = radix - 1;
        do {
            buf[offset + --charPos] = Integer.digits[val & mask];
            val >>>= shift;
        } while (val != 0 && charPos > 0);

        return charPos;
    }

该方法被toUnsignedString0()方法调用,而toUnsignedString0()方法又被toBinaryString()方法、toOctalString()方法、toHexString()方法调用。明白这点是很重要的,因为要把它们关联起来阅读。

关于toUnsignedString0()方法请参考:JDK源码之Integer类—toUnsignedString0()方法

formatUnsignedInt()方法的注释如下:

    /**
     * 将long(视为无符号)格式化为字符串缓冲区
     *
     * @param val    要格式的无符号整数,int类型
     * @param shift  要格式化的基数的log2(十六进制为4,八进制为3,二进制为1)
     * @param buf    要写入的字符缓冲区,是一个字符数组
     * @param offset 目标缓冲区中开始的偏移量
     * @param len    要写入的字符数
     * @return 返回使用的最低字符索引位置
     */
    static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
        // 局部变量,将传入的len作为起始索引位置,倒序向字符数组中插入字符(即从数组的最后一个位置开始向前插入字符)
        int charPos = len;
        /*
            这两行的作用是要得到对应进制数的掩码。
            如果是二进制,因为2的1次方是2,所以shift就是1,那么radix就是1左移1位得到2,mask就是1,对应的二进制就是0001;
            如果是八进制,因为2的3次方是8,所以shift就是3,那么radix就是1左移3位得到8,mask就是7,对应的二进制就是0111;
            如果是十六进制,因为2的4次方是16,所以shift就是4,那么radix就是1左移4位得到16,mask就是15,对应的二进制就是1111;
         */
        int radix = 1 << shift;
        int mask = radix - 1;
        /*
            之前我们求十进制转换成其他进制就是不断除以进制数,最后组合得到的余数
            但如果是转换成八进制或十六进制这种进制数为2的整数次方的进制,有更快的算法,就是先得到二进制,然后再每几位一组转换
            比如将十进制数123转换成八进制:
                第一步,先把123转换成二进制是:0111 1011
                第二步,因为八进制的8是2的3次方,所以将0111 1011每三位一组划分,高位补0,即001 111 011
                第三步,把001 111 011按每一组分别转换成八进制,也就是1 7 3,所以123的八进制表示是173
         */
        do {
            /*
                循环val & mask的过程就像分组转换的过程,每次循环可以将mask对应的位数得到,再利用digits数组转换成对应的字符,
                这个字符就是这次分组的这几位所对应的结果,每次分组得到结果以后把val右移相应的位数,继续下一轮的循环分组。
             */
            /*
                假如val=123,对应二进制是0111 1011,要求转换成八进制,那么mask等于7,锁对应的二进制是111,将val每三位分组后的二进制是001 111 011
                    第一次循环:val&mask保留低三位011,而val>>>=shift就是无符号右移三位,即覆盖原来的低三位,也为下一次循环保留低三位作准备
                        val&mask
                            001 111 011
                           &
                            000 000 111
                           =000 000 011(保留了val的低三位,011所表示的八进制是3,在digits数组中对应的字符是'3')
                       val>>>=shift
                            001 111 011
                         >>>
                            3
                           =000 001 111
                    第二次循环:val&mask保留低三位111,而val>>>=shift就是无符号右移三位,即覆盖原来的低三位,也为下一次循环保留低三位作准备
                        val&mask
                            000 001 111
                           &
                            000 000 111
                           =000 000 111(保留了val的低三位,111所表示的八进制是7,在digits数组中对应的字符是'7')
                       val>>>=shift
                            000 001 111
                         >>>
                            3
                           =000 000 001
                    第三次循环:val&mask保留低三位001,而val>>>=shift就是无符号右移三位,即覆盖原来的低三位,也为下一次循环保留低三位作准备
                        val&mask
                            000 000 001
                           &
                            000 000 111
                           =000 000 001(保留了val的低三位,001所表示的八进制是1,在digits数组中对应的字符是'1')
                       val>>>=shift
                            000 000 001
                         >>>
                            3
                           =000 000 000(val为0时结束循环)

             */
            buf[offset + --charPos] = Integer.digits[val & mask];
            val >>>= shift;
        } while (val != 0 && charPos > 0);

        return charPos;
    }

该方法的核心就是将十进制转换成二进制或八进制或十六进制,不是使用的除数取余法,而是采用更加快捷的位运算。

关于本方法如何将十进制转换成二进制或八进制或十六进制,注释中虽然有示例说明,但下面给出图解。

关于具体将二进制转换成八进制和十六进制就不多言了。

以上是关于JDK源码之Integer类—formatUnsignedInt()方法的主要内容,如果未能解决你的问题,请参考以下文章

JDK源码之Integer类——rotateRight()方法

JDK源码之Integer类——rotateLeft()方法

JDK源码之Integer类——numberOfLeadingZeros()方法

JDK源码之Integer类——numberOfTrailingZeros()方法

JDK源码之Integer类—signum()方法

JDK源码之Integer类—reverseBytes()方法