源码学习系列 —— java.lang.Number

1. Number类介绍

Number是抽象类,是基本数据类型中数值类型(除charboolean)包装类型、大数值类型和原子操作类型(线程安全)的抽象父类。Number类中提供的方法如下:

方法名说明
abstract int intValue()抽象方法,子类实现。提供基本类型int的转换。
abstract long longValue()抽象方法,子类实现。提供基本类型long的转换。
abstract float floatValue()抽象方法,子类实现。提供基本类型float的转换。
abstract double doubleValue()抽象方法,子类实现。提供基本类型double的转换。
byte byteValue()提供基本类型byte的转换,由int intValue()强转。
short shortValue()提供基本类型short的转换,由int intValue()强转。

2. 子类介绍

类全限定名说明
java.util.concurrent.atomic.AtomicInteger
java.util.concurrent.atomic.AtomicLong
java.math.BigDecimal
java.math.BigInteger
java.lang.Byte1字节(8位)整数
java.lang.Double双精度浮点数
java.util.concurrent.atomic.DoubleAccumulator
java.util.concurrent.atomic.DoubleAdder
java.lang.Float单精度浮点数
java.lang.Integer4字节(32位)整数
java.lang.Long8字节(64位)整数
java.util.concurrent.atomic.LongAccumulator
java.util.concurrent.atomic.LongAdder
java.lang.Short2字节(16位)整数
java.util.concurrent.atomic.Striped64仅包内可见抽象子类,

3. 包装类型

常用数值的“==”比较

JVM中有数值常量池,存放[-128, 127]区间内所有数字。常量池中某个数字被引用时,其所有引用对象地址都相同。

  • 基本类型与基本类型比较:值相同,则相同,结果为true
  • 基本类型与包装类型比较:包装类型自动拆箱,值相同,则相同,结果为true
  • 包装类型与包装类型比较:
    其包装类型为(ByteShortIntegerLongFloatDouble,不含父类Number
    • 两包装类型不同时,编译不通过。
    • 两包装类型相同时:
      • 两个变量均为直接赋值时,如Integer i1 = ?; Integer i2 = ?;
        • 取值在[-128, 127]区间内,值相同,则相同,结果为true
        • 取值在[-128, 127]区间外,即(-∞, -128) ∪(127, +∞),值相同,但对象引用不同,结果为false
        • 强制转换其中之一为基本类型,另外一个比较时包装类型自动拆箱,值相同,则相同,结果为true
      • 两个变量其中之一或均使用new关键字赋值时,如Integer i1 = new Integer(?); Integer i2 = new Integer(?);
        • 任何值相同,但对象引用不同,结果为false
        • 强制转换其中之一为基本类型,另外一个比较时包装类型自动拆箱,值相同,则相同,结果为true

示例

public class Main {
    public static void main(String[] args) {
        int i1 = 9;
        int i2 = 9;
        System.out.println("基本类型与基本类型比较,结果为" + (i1 == i2));
        int i3 = 9;
        Integer i4 = 9;
        System.out.println("基本类型与包装类型比较,结果为" + (i3 == i4));
        int i5 = 9;
        Integer i6 = new Integer(9);
        System.out.println("基本类型与包装类型比较(使用new关键字):" + (i5 == i6));
        Long l7 = 9L;
        Integer i8 = 9;
        // Error:(37, 52) java: 不可比较的类型: java.lang.Integer和java.lang.Long
//        System.out.println("两包装类型不同时,编译不通过。" + (l7 == i8));
        Integer i9 = 9;
        Integer i10 = 9;
        System.out.println("两个变量均为直接赋值时,取值在[-128, 127]区间内,值相同,则相同,结果为" + (i3 == i4));
        Integer i11 = 129;
        Integer i12 = 129;
        System.out.println("两个变量均为直接赋值时,取值在[-128, 127]区间外,即(-∞, -128) ∪(127, +∞),值相同,但对象引用不同,结果为" + (i11 == i12));
        Integer i13 = 129;
        Integer i14 = 129;
        System.out.println("两个变量均为直接赋值时,强制转换其中之一为基本类型,另外一个比较时包装类型自动拆箱,值相同,则相同,结果为" + ((int) i13 == i14));
        Integer i15 = 9;
        Integer i16 = new Integer(9);
        System.out.println("两个变量其中之一或均使用new关键字赋值时,任何值相同,但对象引用不同,结果为" + (i15 == i16));
        Integer i17 = new Integer(9);
        Integer i18 = new Integer(9);
        System.out.println("两个变量其中之一或均使用new关键字赋值时,强制转换其中之一为基本类型,另外一个比较时包装类型自动拆箱,值相同,则相同,结果为" + ((int)i15 == i16));
    }
}

结果输出

基本类型与基本类型比较,结果为true
基本类型与包装类型比较,结果为true
基本类型与包装类型比较(使用new关键字)true
//        System.out.println("两包装类型不同时,编译不通过。" + (l7 == i8));
两个变量均为直接赋值时,取值在[-128, 127]区间内,值相同,则相同,结果为true
两个变量均为直接赋值时,取值在[-128, 127]区间外,即(-, -128)(127, +),值相同,但对象引用不同,结果为false
两个变量均为直接赋值时,强制转换其中之一为基本类型,另外一个比较时包装类型自动拆箱,值相同,则相同,结果为true
两个变量其中之一或均使用new关键字赋值时,任何值相同,但对象引用不同,结果为false
两个变量其中之一或均使用new关键字赋值时,强制转换其中之一为基本类型,另外一个比较时包装类型自动拆箱,值相同,则相同,结果为true

与或运算符


简单理解,将10看做truefalse,执行运算时,若两个都为true,结果为true,若其中一个为false,则结果为false。那么10的与运算结果为:

0 & 0 = 0;
0 & 1 = 0; 
1 & 0 = 0; 
1 & 1 = 1;


执行运算时,若两个都为false,结果为false,若其中一个为true,则结果为true。那么10的与运算结果为:

0 & 0 = 0;
0 & 1 = 1; 
1 & 0 = 1; 
1 & 1 = 1;

原码、反码、补码

  • 原码即数字本身的二进制码,最高位(最左第一位)为符号位,最高位为0表示正数,为1表示负数,其余位才是数值的大小。
  • 反码
    • 正数的反码与原码相同;
    • 负数的反码则是对原码取反,符号位不变(保持为1)。
  • 补码
    • 正数的补码与原码相同;
    • 负数的补码则是反码加1

原反补小练

int i = 293;
System.out.println((byte) i); --> 37

i的二进制数‭0001 0010 0101‬,得293的二进制数占9位,超byte约定的8位,因此取原码为0010 0101‬。原码最高位为0,即正数。则反码和补码均为0010 0101‬,转换0010 0101‬10进制得37

int i = 135;
System.out.println((byte) i); --> -121

i的二进制数‭‭1000 0111‬,取原码为1000 0111‬。原码最高位为1,即负数。则
反码为1111 1000
补码为1111 1000 + 1 = 1111 1001
补码最高位丢弃,得0111 1001,转10进制为121,因其为负数,则结果为-121

3.1. Byte

最小数值类型,占用内存一个字节(每字节8位),默认值为零,取值区间为[-128, 127],即[-2^7, 2^7 - 1]。JVM中有数值常量池,存放[-128, 127]区间内所有数字,即Byte的所有值均为常量池中取出。Byte实现java.lang.Comparable接口,提供比较和排序方法。

3.1.1. 静态常量与方法

常量或方法说明
TYPE返回基本类型的Class,byte.class
SIZE返回Byte位数,取值为8
BYTES返回Byte占用字节数,取值为1 ,方法实现为SIZE / Byte.SIZE
String toString(byte b)将某个byte10进制的形式转换为字符串,方法实现为Integer.toString((int)b, 10);
int hashCode(byte value)value强转为int类型,并作为hashCode返回
byte parseByte(String s, int radix)将某个字符串s以指定进制radix的形式解析为byte,方法检查该字符串为[-128, 127]区间上的数字,否则抛出java.lang.NumberFormatException异常
byte parseByte(String s)将某个字符串s10进制的形式解析为byte,方法检查该字符串为[-128, 127]区间上的数字,否则抛出java.lang.NumberFormatException异常
Byte valueOf(byte b)将某个byte转换为包装类型Byte,方法实现为从ByteCahe缓存里取出对应索引的数字ByteCache.cache[(int)b + 128]
Byte valueOf(String s, int radix)将某个字符串s以指定进制radix的形式解析为byte,方法检查该字符串为[-128, 127]区间上的数字,否则抛出java.lang.NumberFormatException异常
byte valueOf(String s)将某个字符串s10进制的形式解析为byte,方法检查该字符串为[-128, 127]区间上的数字,否则抛出java.lang.NumberFormatException异常
Byte decode(String nm)解码某个数字字符串nmByte,程序将根据数字字符串实际内容判断用何种进制(81016)来解析该字符串。方法检查该字符串为-128 ~ 127之间的数字,否则抛出java.lang.NumberFormatException异常
int compare(byte x, byte y)比较两个bytexy之间的大小,方法实现为取两者差值x - y;
int toUnsignedInt(byte x)byte转换为无符号的int类型。方法实现为((int) x) & 0xff
long toUnsignedLong(byte x)byte转换为无符号的long类型。方法实现为((long) x) & 0xff

3.1.2. 构造函数

构造函数说明
Byte(byte value)接收基本类型byte作为值,新建包装类型Byte实例
Byte(String s)接收数字字符串s,并将其以10进制的形式解析为包装类型Byte实例

3.1.3. 私有类 ByteCache

ByteCacheByte.java类中的字节缓存类,采用一个长度为256Byte数组缓存[-128, 127]区间内所有数字。

private static class ByteCache {
    private ByteCache(){}

    static final Byte cache[] = new Byte[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Byte((byte)(i - 128));
    }
}

3.2. Short

占用内存两个字节(每字节8位,共16位),默认值为零,取值区间为[-32768, 32767],即[-2^15, 2^15 - 1]Short实现java.lang.Comparable接口,提供比较和排序方法。

3.2.1. 静态常量与方法

常量或方法说明
TYPE返回基本类型的Class,short.class
SIZE返回Short位数,取值为16
BYTES返回Short占用字节数,取值为2 ,方法实现为SIZE / Byte.SIZE
String toString(short s)将某个short10进制的形式转换为字符串,方法实现为Integer.toString((int)s, 10);
int hashCode(short value)value强转为int类型,并作为hashCode返回
Short parseShort(String s)将某个字符串s10进制的形式解析为short,方法检查该字符串为[-32768, 32767]区间上的数字,否则抛出java.lang.NumberFormatException异常
byte parseShort(String s, int radix)将某个字符串s以指定进制radix的形式解析为short,方法检查该字符串为[-32768, 32767]区间上的数字,否则抛出java.lang.NumberFormatException异常
Short valueOf(short s)将某个short转换为包装类型Short,若s取值在[-32768, 32767]区间,则从ShortCache缓存里取出对应索引的数字ShortCache.cache[(int)b + 128],否则返回new Short(s)
Short valueOf(String s, int radix)将某个字符串s以指定进制radix的形式解析为Short,方法检查该字符串为[-32768, 32767]区间上的数字,否则抛出java.lang.NumberFormatException异常
Short valueOf(String s)将某个字符串s10进制的形式解析为Short,方法检查该字符串为[-32768, 32767]区间上的数字,否则抛出java.lang.NumberFormatException异常
Short decode(String nm)解码某个数字字符串nmShort,程序将根据数字字符串实际内容判断用何种进制(81016)来解析该字符串。方法检查该字符串为[-32768, 32767]区间上的数字,否则抛出java.lang.NumberFormatException异常
int compare(short x, short y)比较两个shortxy之间的大小,方法实现为取两者差值x - y;
short reverseBytes(short i)反转short的字节,非将数字反转,而是反转字节(按8位一组反转),方法实现为(short) (((i & 0xFF00) >> 8) | (i << 8))
int toUnsignedInt(short x)short转换为无符号的int类型。方法实现为((int) x) & 0xffff
long toUnsignedLong(short x)short转换为无符号的int类型。方法实现为((int) x) & 0xffff

3.2.2. 构造函数

构造函数说明
Short(short value)接收基本类型short作为值,新建包装类型Short实例
Short(String s)接收数字字符串s,并将其以10进制的形式解析为包装类型Short实例

3.2.3. 私有类 ShortCache

ShortCacheShort.java类中的字节缓存类,采用一个长度为256Short数组缓存[-128, 127]区间内所有数字。JVM中有数值常量池,存放[-128, 127]区间内所有数字,即Short的所有值在[-128, 127]区间上的均从常量池中取出。

private static class ShortCache {
    private ShortCache(){}

    static final Short cache[] = new Short[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Short((short)(i - 128));
    }
}

3.2.4. 方法解析

3.2.4.1 short reverseBytes(short i)

该方法具体实现为(short) (((i & 0xFF00) >> 8) | (i << 8)),是将short数字按8位一组进行反转。

(short) (((i & 0xFF00) >> 8) | (i << 8))

0xFF0016进制数,其10进制和2进制分别为65280‭‭1111 1111 0000 0000‬,而与操作&则是当0xFF00与上i时,将0 - 00 - 11 - 0对应位置的数清零,将1 - 1对应位置的1保留。>> 8则是将(i & 0xFF00)的结果右移8位,然后或上i左移8位的结果(i << 8)

100为例

Short.reverseBytes((short)100); --> 25600

100用二进制表示形式为0110 010016位则是0000 0000 0110 0100

步骤原式解析结果十进制结果
1100 & 0xFF000000 0000 0110 0100 & ‭‭1111 1111 0000 0000‬0000 0000 0000 00000
2(100 & 0xFF00) >> 80000 0000 0000 0000 >> 80000 0000 0000 00000
3100 << 80000 0000 0110 0100 << 80110 0100 0000 0000‭25600‬
4(100 & 0xFF00) >> 8 | i << 80110 0100 0000 0000 | 0000 0000 0000 00000110 0100 0000 0000‭25600‬

而通常来说将0000 0000 0110 01008位一组反转后得到0110 0100 0000 000010进制数25600也比较直观。

但若是一个比较特殊的数呢?
1234为例

Short.reverseBytes((short)1234); --> -11772

1234用二进制表示形式为‭0100 1101 0010‬16位则是0000 0100 1101 0010‬

步骤原式解析结果十进制结果
11234 & 0xFF000000 0100 1101 0010 & ‭‭1111 1111 0000 0000‬0000 0100 0000 00001024
2(1234 & 0xFF00) >> 80000 0100 0000 0000 >> 80000 0000 0000 01004
31234 << 80000 0100 1101 0010 << 80100 1101 0010 0000 0000‭315904
4(1234 & 0xFF00) >> 8 | i << 80000 0000 0000 0100 | 0100 1101 0010 0000 00000100 1101 0010 0000 0100‭‭315908‬
5(short)(1234 & 0xFF00) >> 8 | i << 8---11772

‭‭315908‬这个数已经超出[-32768, 32767]区间了,很明显强转类型为short会丢失精度。那么就要根据二进制原码、反码、补码来计算‭‭315908‬被强转之后的值,按照之前了解的原码、反码和补码的定义。

已知‭‭315908‬的二进制数为0100 1101 0010 0000 0100,强转short须截取16位的数得原码1101 0010 0000 0100
由于原码第一位为1,表示为负数。需要先计算反码然后得出补码。

原码为1101 0010 0000 0100
反码为1010 1101 1111 1011
补码为1010 1101 1111 1011 + 1 = 1010 1101 1111 1100
补码最高位丢弃,得0010 1101 1111 1100,转10进制为11772,因其为负数,则结果为-11772

3.3 Integer

占用内存四个字节(每字节8位,共32位),默认值为零,取值区间为[-2147483648, 2147483647],即[-2^31, 2^31 - 1],分别用16进制数0x800000000x7fffffff表示。Integer实现java.lang.Comparable接口,提供比较和排序方法。

3.3.1. 静态常量与方法

常量或方法说明
TYPE返回基本类型的Class,int.class
SIZE返回Byte位数,取值为32
BYTES返回Byte占用字节数,取值为4 ,方法实现为SIZE / Byte.SIZE
String toString(int i, int radix)将某个int以指定进制的形式转换为字符串
String toString(int i)将某个int10进制的形式转换为字符串
int hashCode(int value)value强转为int类型,并作为hashCode返回
int parseInt(String s)将某个字符串s10进制的形式解析为int
int parseInt(String s, int radix)将某个字符串s以指定进制radix的形式解析为int
int parseUnsignedInt(String s)将某个字符串s10进制radix的形式解析为无符号的int
int parseUnsignedInt(String s, int radix)将某个字符串s以指定进制radix的形式解析为无符号的int
Integer valueOf(String s)将某个字符串s以10进制radix的形式解析为包装类型Integer
Integer valueOf(String s, int radix)将某个字符串s以指定进制radix的形式解析为包装类型Integer,方法实现为Integer.valueOf(parseInt(s,radix))
Integer valueOf(int i)int数字转换为包装类型Integer
Integer getInteger(String nm)从系统属性中获取指定属性名称nm对应的值,并解码为Integer,若未找到该值或解码失败,则返回null
Integer getInteger(String nm, int val)从系统属性中获取指定属性名称nm对应的值,并解码为Integer,若未找到该值或解码失败,则返回传入的包装类型的默认值val
Integer getInteger(String nm, Integer val)从系统属性中获取指定属性名称nm对应的值,并解码为Integer,若未找到该值或解码失败,则返回传入的默认值val
Integer decode(String nm)解码某个数字字符串nmInteger,程序将根据数字字符串实际内容判断用何种进制(81016)来解析该字符串
int compare(int x, int y)比较两个intxy之间的大小,与short不同,方法只会返回1, 0, -1。方法实现为(x < y) ? -1 : ((x == y) ? 0 : 1)
int compareUnsigned(int x, int y)比较两个intxy之间的大小,方法实现为compare(x + MIN_VALUE, y + MIN_VALUE)
long toUnsignedLong(int i)int转换为无符号的long,方法实现为((long) x) & 0xffffffffL
int divideUnsigned(int dividend, int divisor)将传入的两个数转换为无符号的long后相除,再强转回int返回。方法实现为(int)(toUnsignedLong(dividend) / toUnsignedLong(divisor))
int remainderUnsigned(int dividend, int divisor)将传入的两个数转换为无符号的long后求余,再强转回int返回。方法实现为(int)(toUnsignedLong(dividend) / toUnsignedLong(divisor))
int highestOneBit(int i)求最高位权值,将i的二进制数除最高位外的所有数1替换为0后转为int返回。该方法总是返回2n次方,n ∈ Z,0的权值总为0
int lowestOneBit(int i)求最低位权值,将i的二进制数除最低位外的所有数1替换为0后转为int返回。该方法总是返回2n次方,n ∈ Z,0的权值总为0。方法实现为i & -i
int numberOfLeadingZeros(int i)返回i的二进制数中,最高位前0的数量,总位数(int的位数为32),即从左往右数,遇到第一个数字1后停下所得到的数量(不含1),数字0的数量总为32,负数因其表现形式为最高位前的数全是1,所以负数的最高位前0的数量总为0
int numberOfTrailingZeros(int i)返回i的二进制数中,最低位开始连续为0的数量,即从右往左数,遇到第一个数字1后停下所得到的数量(不含1),数字0的数量总为32,绝对值相同的正负数的数量相同
int bitCount(int i)返回i的二进制中,各位数为1的总数量,0的总数量总是为0,负数因其表现形式为最高位前的数全是1,所以这些为1的数也会算上
int rotateLeft(int i, int distance)左旋,方法实现为(i << distance) | (i >>> -distance)
int rotateRight(int i, int distance)右旋,方法实现为(i >>> distance) | (i << -distance)
int reverse(int i, int distance)反转
int signum(int i)判断正负数,负数返回-1,正数返回0,零总是返回0,方法实现为(i >> 31) | (-i >>> 31)
int reverseBytes(int i)按字节反转,方法实现为((i >>> 24)) | ((i >> 8) & 0xFF00) | ((i << 8) & 0xFF0000) | ((i << 24))
int sum(int a, int b)求和。方法实现为a + b,可用于Lambda表达式Integer::sum
int max(int a, int b)返回两个数中,比较大的一个。方法实现为Math.max(a, b),可用于Lambda表达式Integer::max
int min(int a, int b)返回两个数中,比较大的一个。方法实现为Math.min(a, b),可用于Lambda表达式Integer::max
String toBinaryString(int i)int转换为32位长度的二进制字符串,负数的最高位前会填充1。方法实现为toUnsignedString0(i, 1)
String toOctalString(int i)int转换为八进制字符串。方法实现为toUnsignedString0(i, 3)
String toHexString(int i)int转换为十六进制字符串。方法实现为toUnsignedString0(i, 4)
String toUnsignedString(int i)int转换为无符号long后再输出为字符串。方法实现为Long.toUnsignedString(toUnsignedLong(i))
String toUnsignedString(int i, int radix)int转换为无符号long后再输出为指定进制数的字符串。方法实现为Long.toUnsignedString(toUnsignedLong(i), radix)

3.3.2. 构造函数

构造函数说明
Integer(int value)接收基本类型int作为值,新建包装类型Integer实例
Integer(String s)接收数字字符串s,并将其以10进制的形式解析为包装类型Integer实例

3.3.3. 私有类 IntegerCache ,调整数字常量池上限

IntegerCacheInteger.java类中的数值缓存类,同样也是采用一个长度为256Integer数组缓存[-128, 127]区间内所有数字,但它与ByteShort不同的是,JDK提供通过设置虚拟机参数java.lang.Integer.IntegerCache.high来调整常量池中的最大值。

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}
java.lang.Integer.IntegerCache.high

先在IDEA中设置JVM参数,-Djava.lang.Integer.IntegerCache.high=400
IDEA中设置JVM参数

Integer i1 = 399;
Integer i2 = 399;
System.out.println(i1 == i2); --> true

结果如常用数值的“==”比较中描述的
两个变量均为直接赋值时,取值在[-128, 127]区间内,值相同,则相同,结果为true
两个变量均为直接赋值时,取值在[-128, 127]区间外,即(-∞, -128) ∪(127, +∞),值相同,但对象引用不同,结果为false
取值区间将被调整至
在[-128, 400]区间内,值相同,则相同,结果为true
在[-128, 400]区间外,即(-∞, -128) ∪(400, +∞),值相同,但对象引用不同,结果为false

3.4.3. 方法解析

3.4.3.1 String toString(int i, int radix)

ByteShort类中的toString(?)方法均由此方法实现。其代码如

    public static String toString(int i, int radix) {
        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
            radix = 10;

        /* Use the faster version */
        if (radix == 10) {
            return toString(i);
        }

        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. 检查其进制数是否属于[2, 36]区间,若不属于,则默认转换进制数设置为10
  2. 若进制数为10,调用String toString(int i),见下文;
  3. 检查传入数字i是否负数,若非负数,则转换为负数;
  4. 进入while循环,按-(i % radix)获取下标,并从常量数组digits中取得对应字符,然后i = i / radix

4步比较笼统,所以先来复习下10进制转其他进制的算法

  • 十进制转二进制,先取十进制数x2的结果,在依次将x除以2(取整并舍弃小数点,直至其小于2)后余2,记录其结果最后倒排该结果,得二进制。以333为例
步骤十进制数除以2余2
1333-1
23331660
3166831
483411
541200
620100
71051
8520
9211

倒排后得二进制结果:1 0100 1101

  • 十进制转八进制,先取十进制数x8的结果,在依次将x除以8(取整并舍弃小数点,直至其小于8)后余8,记录其结果,最后倒排该结果,得八进制。以333为例
步骤十进制数除以8余8
1333-5
2333411
34155

倒排后得八进制结果:515

  • 十进制转十六进制,先取十进制数x16的结果,再依次将x除以16(取整并舍弃小数点,直至其小于16)后余16,记录其结果,最后倒排该结果,得十六进制。以333为例
步骤十进制数除以16余16
1333-13(d)
2333204
32011

倒排后得十六进制结果:14d

根据以上过程能发现规律:先取十进制数x余进制数的结果,再依次将x除以进制数(取整并舍弃小数点,直至其小于进制数)后余进制数,记录结果。当十进制数x通过除以进制数至小于进制数并求余后,不再执行下一步被除求余的操作,接着倒排结果,得到进制数对应值。

简单编写一段代码来计算十进制转其它进制的字符串

首先要看下Integer类中提供的常量数组digits,它保证[0, 36)区间内,能够通过下标取得对应的值,如十六进制的10对应a、三十六进制的35对应z。之所以会有z,是因为Java支持2 ~ 36进制的转换。

final static 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'
    };
public static String toString(int i, int radix) {
     boolean negative = (i < 0);
     if (negative) {
         i = -i;
     }
     StringBuilder stringBuilder = new StringBuilder(33);
     stringBuilder.append(digits[i % radix]);
     while (i >= radix) {
         i = i / radix;
         stringBuilder.append(digits[i % radix]);
     }
     if(negative) {
         stringBuilder.append("-");
     }
     return stringBuilder.reverse().toString();
 }

上述代码就是逐步记录十进制数i除以进制数并余进制数后的结果,最后通过StringBuilder.reverse()方法反转字符串得到进制数对应字符串值。对比JDK中的源码,除了一个正数循环和另一个负数循环以外似乎没有其它差异,毕竟StringBuilder也是可以替换成char[]来实现的。

3.4.3.2 String toString(int i)

将数字i10进制形式转换为字符串,由于日常使用中,int类型数字总为10进制,因此该方法是否真的有意义值得推敲,且实现代码复杂。
在单线程一亿个数循环转换测试中发现i + ""转字符串消耗的时间比Integer.toString(i)200 ~ 600毫秒甚至更多,因此推荐使用i + ""的方式将数字转换成十进制字符串,而不是Integer.toString(i)

当然其中的移位运算值得一看。

static void getChars(int i, int index, char[] buf) {
   int q, r;
   int charPos = index;
   char sign = 0;

   if (i < 0) {
       sign = '-';
       i = -i;
   }

   // Generate two digits per iteration
   while (i >= 65536) {
       q = i / 100;
   // really: r = i - (q * 100);
       r = i - ((q << 6) + (q << 5) + (q << 2));
       i = q;
       buf [--charPos] = DigitOnes[r];
       buf [--charPos] = DigitTens[r];
   }

   // Fall thru to fast mode for smaller numbers
   // assert(i <= 65536, i);
   for (;;) {
       q = (i * 52429) >>> (16+3);
       r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
       buf [--charPos] = digits [r];
       i = q;
       if (i == 0) break;
   }
   if (sign != 0) {
       buf [--charPos] = sign;
   }
}
String str = null;
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
    str = i + "";
}
System.out.println(System.currentTimeMillis() - startTime);
startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
    str = Integer.toString(i);
}

--------------------------------------- 待续 -------------------------

3.4.3.3 Integer parseInt(int i, int radix)
3.4.3.4 Integer parseUnsignedInt(int i, int radix)
3.4.3.4 Integer valueOf(int i)
3.4.3.4 Integer getInteger(String nm, int val)
3.4.3.4 String toUnsignedLong(int i)
3.4.3.4 int divideUnsigned(int dividend, int divisor)
3.4.3.4 int remainderUnsigned(int dividend, int divisor)
3.4.3.4 int highestOneBit(int i)
3.4.3.4 int lowestOneBit(int i)
3.4.3.4 int numberOfLeadingZeros(int i)
3.4.3.4 int numberOfTrailingZeros(int i)
3.4.3.4 int bitCount(int i)
3.4.3.4 int rotateLeft(int i, int distance)
3.4.3.4 int rotateRight(int i, int distance)
3.4.3.4 int reverse(int i, int distance)
3.4.3.4 int signum(int i)
3.4.3.4 int reverseBytes(int i)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值