Integer 的基本实现
Integer 的使用
Integer 封装的操作
Integer 的基本实现
基本描述:
Integer 是对原生基本类型 int 的封装,其定义 value 来存储值和一些用于描述 int 的信息
int value;//int
int SIZE = 32;// 1 位正负标识 +31 位数据
int BYTES = SIZE / Byte.SIZE;// 所占字节
int MIN_VALUE = 0x80000000;// 最小值,32 个 1
int MAX_VALUE = 0x7fffffff;// 最大值,0+31 个 1
构造函数:
允许通过 String 和 int 入参来为 value 赋值,但是两个构造函数都已弃用
通过注释可以看到,推荐通过 valueOf()的方法来返回一个 Integer
/**
* @deprecated
* It is rarely appropriate to use this constructor. The static factory
* {@link #valueOf(int)} is generally a better choice, as it is
* likely to yield significantly better space and time performance.
*/
@Deprecated(since="9")
public Integer(int value) {this.value = value;}
/**
* @deprecated
* It is rarely appropriate to use this constructor.
* Use {@link #parseInt(String)} to convert a string to a
* {@code int} primitive, or use {@link #valueOf(String)}
* to convert a string to an {@code Integer} object.
*/
@Deprecated(since="9")
public Integer(String s) throws NumberFormatException {this.value = parseInt(s, 10);
}
使用推荐的方法获取 Integer 实例和构造方法有何不同?
//----------------------int 入参 ------------------
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
//----------------------String 入参 ------------------
public static Integer valueOf(String s) throws NumberFormatException {return Integer.valueOf(parseInt(s, 10));
}
//radix 表示进制,取值范围为[2, 36]
public static Integer valueOf(String s, int radix) throws NumberFormatException {return Integer.valueOf(parseInt(s,radix));
}
- int 入参
如果入参中的 int 在 IntegerCache 内部类的 Integer cache[]中存在则返回数组中的 Integer 否则通过构造函数创建(弃用的那个)
- String 入参
通过 parseInt(s,radix)方法解析字符串,返回 int 值
radix 参数表示字符串转换的 int 值的进制,其取值范围为[2,36]
解析 IntegerCache 和 parseInt 的实现
IntegerCache
//The cache is initialized on first usage.
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;
//The size of the cache may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
String integerCacheHighPropValue =
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() {}
}
IntegerCache 是一个私有静态内部类该类内部定义了一个数组 Integer cache[],数组内的数据由 -128 起始,默认至 127 为止(byte 的范围)
该数组的最大值可通过在 jvm 中设置
-XX:AutoBoxCacheMax=<size> 来设置其大小
数组 cache[128]为 0,valueof(int)参数的值符合这个范围都会直接从数组中返回 Integer
有意思的是 valueof(int)是 @HotSpotIntrinsicCandidate 的,关于它的描述是这样的:
JDK 的源码中,被 @HotSpotIntrinsicCandidate 标注的方法,在 HotSpot 中都有一套高效的实现,该高效实现基于 CPU 指令,运行时,HotSpot 维护的高效实现会替代 JDK 的源码实现,从而获得更高的效率。
估计这就是推荐使用的主要原因吧!
parseInt
public static int parseInt(String s, int radix)
throws NumberFormatException
{
...
boolean negative = false;// 正负标识
int i = 0, len = s.length();
int limit = -Integer.MAX_VALUE;
if (len > 0) {char firstChar = s.charAt(0);
// 判断输入的字符串是否为 "-" 开头
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+') {throw NumberFormatException.forInputString(s);
}
if (len == 1) { // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
}
i++;
}
// 转化逻辑
int multmin = limit / radix;
int result = 0;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
int digit = Character.digit(s.charAt(i++), radix);
if (digit < 0 || result < multmin) {throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {throw NumberFormatException.forInputString(s);
}
result -= digit;
}
return negative ? result : -result;
} else {throw NumberFormatException.forInputString(s);
}
}
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'
};
字符串转化为 int 的关键在于 digits 数组,以 16 进制为例,用 0 …9,a…f 表示 0 到 15,满 16 才会进 1。也就是超过 10 进制以后,大于 10 的数要使用 a 开始的字母表示,但是字母只有 26 个,进制又必须从 2 开始,故进制的取值范围也就定义为[2, 36]
故入参的字符串 s 也必须符合 digits 数组中的元素以及额外的只可能存在第一位 ”+” 或者 ”-“
parseInt 的转化逻辑为:
在每次循环中
- 取出 digit,确定进制后转化的 int 数
- 通过 result *= radix; 把上一次循环的数据进一位
- 通过 result -= digit; 把当前的数据加入 result
然后返回结果,通过:
return negative ? result : -result;
Integer 的使用
int a = 5;
Integer w = 6;
Integer test = Integer.valueOf(w);
int testP = Integer.valueOf(a);
转化成对应的字节码,则
- int a = 5
0: iconst_5
1: istore_1
直接将自然数压栈
-
Integer w = 6
2: bipush 6
4: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
7: astore_2
调用 Integer 的静态方法 valueof(6)得到 Integer 实例
-
Integer test = Integer.valueOf(w)
8: aload_2
9: invokevirtual #3 // Method java/lang/Integer.intValue:()I
12: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
15: astore_3
获取操作数栈中 w 的引用,调用 intValue 返回 int 值,再通过 valueof 获取 Integer 实例
-
int testP = Integer.valueOf(a)
16: iload_1
17: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
20: invokevirtual #3 // Method java/lang/Integer.intValue:()I
获取操作数栈中的 a,调用 valueof 获取 Integer 实例,再通过 intValue 返回 int 值
由此可知,对于基本类型的封装类,编译器会自动调用其一些方法来实现用户操作的简化!
Integer 封装的操作
Object 虚函数的实现
父类 Number 的虚函数实现
字节操作
Object 虚函数的实现
public boolean equals(Object obj) {if (obj instanceof Integer) {return value == ((Integer)obj).intValue();}
return false;
}
public static int hashCode(int value) {return value;}
public static String toString(int i) {int size = stringSize(i);
if (COMPACT_STRINGS) {byte[] buf = new byte[size];
getChars(i, size, buf);
return new String(buf, LATIN1);
} else {byte[] buf = new byte[size * 2];
StringUTF16.getChars(i, size, buf);
return new String(buf, UTF16);
}
}
- equals
通过 Integer 的 intValue 获取入参的 Integer 封装的 int 值并与 value 进行 == 寻址判断
- hashCode
hashCode 返回的就是一个 int 值,故直接使用 value 本身
- toString
使用 char 数组做中转,通过 String 实例化一个 String 实例
根据是否开启压缩机制判断使用的是 LATIN1 还是 UTF16
父类 Number 的虚函数实现
public byte byteValue() {return (byte)value;
}
public double doubleValue() {return (double)value;
}
public float floatValue() {return (float)value;
}
public int intValue() {return value;}
public long longValue() {return (long)value;
}
public short shortValue() {return (short)value;
}
只是对 value 进行强转
字节操作
计算 int 二进制形式左 (右) 侧有几个 0,遇到 1 就停止计数
计算 int 二进制形式 1 的数量
左(右)移二进制形式
按位 (字节) 置换
计算 int 二进制形式左 (右) 侧有几个 0,遇到 1 就停止计数
// 左侧
public static int numberOfLeadingZeros(int i) {
// HD, Count leading 0's
if (i <= 0)
return i == 0 ? 32 : 0;
int n = 31;
if (i >= 1 << 16) {n -= 16; i >>>= 16;}
if (i >= 1 << 8) {n -= 8; i >>>= 8;}
if (i >= 1 << 4) {n -= 4; i >>>= 4;}
if (i >= 1 << 2) {n -= 2; i >>>= 2;}
return n - (i >>> 1);
}
// 右侧
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);
}
- 左侧:numberOfLeadingZeros
1 负数 1 标识,左侧无 0,0 全为 0,直接返回 32(int 为 32 位)
2 通过 1 << 16 判断,判断条件为是否比它大,左边 16 位是否全为 0,决定接下来操作左或右半边
3 再通过 i << 8,4,2,1 折半再折半计算出不为 0 的数字的位置,从而得出 0 的数量
- 右侧:numberOfTrailingZeros
通过 i <<16,不为 0 则右边有 1,再 i << 8,4,2,1,判断出右边数起的第一个 1,从而计算出 0 的数量
计算 int 二进制形式 1 的数量
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;
}
- 0x5 = 0101,通过做 & 运算记录双数位的数据情况
0x3 = 0011,通过做 & 运算记录后两位的数据情况
0x0f = 0000 1111,通过做 & 运算记录后四位的数据情况
- 1 int 的二进制形式的可能有 00,01,10,11
先做 >>> 右移一位再与 01 做 & 运算,记录了两位二进制左边数字的 1 的数量,再用原来的二进制数减去记录的值
如 11:11-01=10(11 有两个 1)
2 经过第一步计算,记录了以两位数为单位的 1 的数量
把第一步的结果与 0011 做 & 运算得到四位二进制结果的后两位计算,0011 再与四位二进制结果 >>> 右移两位计算前两位的结果,再把其相加得到四位数中 1 的数量
如 1011
1011 – 0101 = 0110
0001 + 0010 = 0011(1011 有三个 1)
3 i + (i >>> 4),i + (i >>> 8),i + (i >>> 16)分别把得到的上一步计算的结果整合计算
计算完成后记录结果的有效位数只有右边八位,32 位数一共最多 32 个 1,所以实际的有效位数只有右边 6 位
左 (右) 移二进制形式
public static int rotateLeft(int i, int distance) {return (i << distance) | (i >>> -distance);
}
public static int rotateRight(int i, int distance) {return (i >>> distance) | (i << -distance);
}
- 移动
调用 << 或 >> 运算符移动,同时通过 | >>> -distance 得到移动消逝的数据,并将其放在补 0 的位置
-
-distance 表示移动 -distance 负数的表现形式 int 截取 5 位,long 截取 6 位,如 - 1 为 32 个 1,截取 5 位为 1 1111,为 31,也就是不算位移,移动的“路程”是 32,正好把移出的数据再补回补 0 的地方
按位 (字节) 置换
public static int reverseBytes(int i) {return (i << 24) |
((i & 0xff00) << 8) |
((i >>> 8) & 0xff00) |
(i >>> 24);
}
public static int reverse(int i) {
// HD, Figure 7-1
i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
return reverseBytes(i);
}
- 按字节置换:reverseBytes
i << 24 与 i >>> 24 做 | 运算得到最左右两边的置换
0xff00 二进制形式为 1111 1111 0000 0000
正好用来处理中间左八位和右八位的交换,主要是 & 和移动的先后来实现不同的位的清零
- 按位置换:reverse
1 使用 01 来记录两位二进制中的一位,再通过移动记录另一位,做 | 运算的会把两位的二进制数交换位置
2 通过 0011 来交换四位中的前两位和后两位
3 通过 0000 1111 来交换前四位和后四位
4 通过前三步实现交换每 8 位的循序,再通过按字节置换交换全部的顺序
后话
Integer 中还有关于
static final byte[] DigitTens = {
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
} ;
static final byte[] DigitOnes = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
} ;
这两个数组的应用和字符和 byte 之间转换的精彩实现,有时间会记录。