您的位置 首页 java

源码阅读Long–JAVA成长之路

Long类型是java八大基本数据类型long的包装类,当数值使得Integer无法表示时我们都会想到Long类型,现在我们窥探一下它的源码吧~

类图

源码阅读Long--JAVA成长之路

public final class Long extends Number implements Comparable<Long> 1 

通过类图和源码我们可以知道Long是不可被继承的,并且Long类型的实例对象是可以比较的。由于Long继承了Number(这是一个抽象类),所以Long重写了其所有形如xxxValue的方法。
源码阅读Long--JAVA成长之路

成员变量

public static final long MIN_VALUE = 0x8000000000000000L;//-2^63public static final long MAX_VALUE = 0x7fffffffffffffffL;//2^63 - 1public static final Class<Long>     TYPE = (Class<Long>) Class.getPrimitiveClass("long");//获取Long的classprivate final long value;public static final int SIZE = 64;//表示二进制补码形式的long值的位数,64位public static final int BYTES = SIZE / Byte.SIZE;//表示二进制补码形式的long值的字节数,8字节123456 

Long对象的值保持在value中,并且value是不可变的。

构造方法

public Long(long value) {this.value = value;}public Long(String s) throws NumberFormatException {this.value = parseLong(s, 10);}123456 

Long有两个构造方法,一个接受long类型数据将其赋值给value,另一个接受数字字符串,不同于Byte,Short中使用 Integer.parseInt(s, radix); 进行数值转换(因为Long太大了,Integer处理不了_),下面我们仔细阅读一下 parseLong 吧!

public static long parseLong(String s, int radix) throws NumberFormatException{if (s == null) {throw new NumberFormatException("null");}//转换进制需要在允许的进制范围内(2~36)if (radix < Character.MIN_RADIX) {throw new NumberFormatException("radix " + radix +" less than Character.MIN_RADIX");}if (radix > Character.MAX_RADIX) {throw new NumberFormatException("radix " + radix +" greater than Character.MAX_RADIX");}long result = 0;boolean negative = false;//是否为负数标志,默认认为是负数int i = 0, len = s.length();long limit = -Long.MAX_VALUE;long multmin;int digit;if (len > 0) {char firstChar = s.charAt(0);//取得第一个字符,if (firstChar < '0') { // 判断第一个字符为何物?只能是"+" 或 "-"if (firstChar == '-') {negative = true;//这是个负数呀!标志置为true。limit = Long.MIN_VALUE;//设置负数的下限为Long类型的下限。} else if (firstChar != '+')throw NumberFormatException.forInputString(s);//第一个字符既不是'-',也不是'+'字符串格式错误,抛出异常if (len == 1) // Cannot have lone "+" or "-"throw NumberFormatException.forInputString(s);//字符串长度为1,但是唯一字符却为'-'或'+',这显然不符合要求,抛出异常i++;//从第二个字符开始转换数字}multmin = limit / radix;while (i < len) {// Accumulating negatively avoids surprises near MAX_VALUEdigit = Character.digit(s.charAt(i++),radix);//如果字符不是在指定进制中合法的数字(二进制合法数字为0,1,八进制合法数字为0~7,十进制为0~9)则返回-1if (digit < 0) {throw NumberFormatException.forInputString(s);//不是合法数字,抛出异常}if (result < multmin) {//溢出throw NumberFormatException.forInputString(s);}result *= radix;//与1*10^2+1*10^1+1*10^0 = 111思路不同,这里的转换方式为:(1*10+1)*10+1 = 111,即乘以进制,再加上下一个数值//如果看过String的hashcode函数的源码就能理解啦!if (result < limit + digit) {//会溢出throw NumberFormatException.forInputString(s);}result -= digit;//long类型的转换全部转换为负数来操作,因为如果字符串表示的值为Long.MIN_VALUE,使用正数会溢出}} else {throw NumberFormatException.forInputString(s);//传入的字符串为空串,直接抛出异常}return negative ? result : -result;//再根据正负标志转换为正确的值}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 

valueOf

public static Long valueOf(long l) {final int offset = 128;if (l >= -128 && l <= 127) { // will cachereturn LongCache.cache[(int)l + offset];//取出缓存中的值}return new Long(l);}public static Long valueOf(String s) throws NumberFormatException{return Long.valueOf(parseLong(s, 10));}public static Long valueOf(String s, int radix) throws NumberFormatException {return Long.valueOf(parseLong(s, radix));}private static class LongCache {private LongCache(){}static final Long cache[] = new Long[-(-128) + 127 + 1];static {for(int i = 0; i < cache.length; i++)cache[i] = new Long(i - 128);}}1234567891011121314151617181920212223242526 

Long同样能够缓存-128~127之间的数字,并且这个大小是不可调的,而Integer的Cache是可以调整的。如果传递的值在缓存区间则从缓存中取值,否则新创建实例对象。所以如果只是单纯需要获取Long类型的小数值推荐使用 valueOf 方法,它比构造函数创建实例对象具备更好的时间和空间效率。

decode

Long类型的 decode 方法又无法沾 Integer.decode 的光了,自己实现吧!

public static Long decode(String nm) throws NumberFormatException {int radix = 10;int index = 0;boolean negative = false;Long result;if (nm.length() == 0)throw new NumberFormatException("Zero length string");//空串,抛异常char firstChar = nm.charAt(0);//取出首字符,需要判断正负呀^_^// Handle sign, if presentif (firstChar == '-') {negative = true;//这是个负数index++;//判断下一个字符} else if (firstChar == '+')index++;// Handle radix specifier, if present//0x,0X,#开头的都是16进制if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {index += 2;radix = 16;}else if (nm.startsWith("#", index)) {index ++;radix = 16;}else if (nm.startsWith("0", index) && nm.length() > 1 + index) {//0开头的为8进制index ++;radix = 8;}if (nm.startsWith("-", index) || nm.startsWith("+", index))throw new NumberFormatException("Sign character in wrong position");//符号放错位置了,只能放在开头呦try {result = Long.valueOf(nm.substring(index), radix);//借助valueOf来转换字符串返回Long类型的值,注意这里的转换都是按照正数来进行的result = negative ? Long.valueOf(-result.longValue()) : result;} catch (NumberFormatException e) {//如果这个字符串表示的值为Long.MIN_VALUE,上面的转换会抛出异常,我们需要将数字和符号组合起来再次使用valueOf进行转换String constant = negative ? ("-" + nm.substring(index)) : nm.substring(index);result = Long.valueOf(constant, radix);}return result;}1234567891011121314151617181920212223242526272829303132333435363738394041424344 

xxxValue

public byte byteValue() {    return (byte)value;}public short shortValue() {    return (short)value;}public float floatValue() {    return (float)value;}public double doubleValue() {    return (double)value;}public int intValue() {    return (int)value;}public long longValue() {    return value;}123456789101112131415161718 

这六个方法都是继承自Number类,将value值强转为对应的类型。

hashCode

public int hashCode() {return Long.hashCode(value);}public static int hashCode(long value) {return (int)(value ^ (value >>> 32));}123456 

首先将long型值无符号右移32位,再和原来的值进行异或运算,最后返回int类型值。Long类型的数值范围比int类型的大多了,将Long类型的hash值用int表示可想而知会产生很多冲突呢!

Long l = new Long(Long.MAX_VALUE);System.out.println(l.hashCode());//-2147483648Long l2 = new Long(Long.MIN_VALUE);System.out.println(l2.hashCode());//-2147483648//发生hash冲突12345 

equals

public boolean equals(Object obj) {if (obj instanceof Long) {return value == ((Long)obj).longValue();}return false;}123456 

首先判断obj是否为Long的实例,再判断value是否相同。

compareTo

public int compareTo(Long anotherLong) {return compare(this.value, anotherLong.value);}public static int compare(long x, long y) {return (x < y) ? -1 : ((x == y) ? 0 : 1);123456 

返回值大于0,前者大于后者,等于0两者相等,小于0前者小于后者。

toXXString

public static String toUnsignedString(long i, int radix) public static String toString(long i)public static String toHexString(long i)public static String toOctalString(long i)public static String toBinaryString(long i)12345 

Long提供了无符号串的转换,十进制串,二进制串,十六进制串和八进制串的转换。 这些方法都借助了toUnsignedString0这一方法,在文章末尾介绍此方法。

bitCount

public static int bitCount(long i) {i = i - ((i >>> 1) & 0x5555555555555555L);i = (i & 0x3333333333333333L) + ((i >>> 2) & 0x3333333333333333L);i = (i + (i >>> 4)) & 0x0f0f0f0f0f0f0f0fL;i = i + (i >>> 8);i = i + (i >>> 16);i = i + (i >>> 32);return (int)i & 0x7f;}123456789 

返回二进制中1的个数。先将重要的列出来,0x5555555555555555L等于0101010101010101010101010101010101010101010101010101010101010101,0x3333333333333333L等于0011001100110011001100110011001100110011001100110011001100110011,0x0f0f0f0f0f0f0f0fL等于0000111100001111000011110000111100001111000011110000111100001111。它的核心思想就是先每两位一组统计看有多少个1,比如10011111则每两位有1、1、2、2个1,记为01011010,然后再算每四位一组看有多少个1,而01011010则每四位有2、4个1,记为00100100,接着每8位一组就为00000110,接着16位,32位,64位,最终在与0x7f进行与运算,得到的数即为1的个数。【来自这里哦!】

highestOneBit

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

该方法返回i的二进制中最高位为1,其他全为0的值。比如5的二进制位101,最高位为1,其他位为0则为100,即为4。如果传递过来的值为0,则返回0,如果传递的值为负数则 固定返回-9223372036854775808(Long.MIN_VALUE)

lowestOneBit

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

该方法返回i的二进制中最低位为1,其他全为0的值。

numberOfLeadingZeros

public static int numberOfLeadingZeros(long i) {// HD, Figure 5-6 if (i == 0)return 64;int n = 1;int x = (int)(i >>> 32);if (x == 0) { n += 32; x = (int)i; }if (x >>> 16 == 0) { n += 16; x <<= 16; }if (x >>> 24 == 0) { n +=  8; x <<=  8; }if (x >>> 28 == 0) { n +=  4; x <<=  4; }if (x >>> 30 == 0) { n +=  2; x <<=  2; }n -= x >>> 31;return n;}1234567891011121314 

该方法返回二进制中从左至右0的个数(遇到第一个1结束)。这里处理其实是体现了二分查找思想的,先看高32位是否为0,是的话则至少有32个0,否则左移16位继续往下判断,接着右移24位看是不是为0,是的话则至少有16+8=24个0,以此类推,直到最后得到结果。

numberOfTrailingZeros

public static int numberOfTrailingZeros(long i) {// HD, Figure 5-14int x, y;if (i == 0) return 64;int n = 63;y = (int)i; if (y != 0) { n = n -32; x = y; } else x = (int)(i>>>32);y = x <<16; if (y != 0) { n = n -16; x = y; }y = x << 8; if (y != 0) { n = n - 8; x = y; }y = x << 4; if (y != 0) { n = n - 4; x = y; }y = x << 2; if (y != 0) { n = n - 2; x = y; }return n - ((x << 1) >>> 31);}123456789101112 

该方法返回二进制中从右至0的个数(遇到第一个1结束)。

reverse

public static long reverse(long i) {i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;i = (i << 48) | ((i & 0xffff0000L) << 16) | ((i >>> 16) & 0xffff0000L) | (i >>> 48);return i;}12345678 

此方法将i进行反转,反转就是第1位与第64位对调,第二位与第63位对调,以此类推。它的核心思想是先将相邻两位进行对换,比如10100111对换01011011,接着再将相邻四位进行对换,对换后为10101101,接着将相邻八位进行对换,最后把64位中中间的32位对换,然后最高16位再和最低16位对换。【来自这里哦!】

toUnsignedString0

/** * val 待格式化的值 * shift 格式化移位数,4表示16进制,3表示八进制,1表示二进制 */static String toUnsignedString0(long val, int shift) {// assert shift > 0 && shift <=5 : "Illegal shift value";int mag = Long.SIZE - Long.numberOfLeadingZeros(val);//计算除去自左至右的0外的数目,例如5,自左至右有61个0,则mag=64-61=3int chars = Math.max(((mag + (shift - 1)) / shift), 1);//计算val使用指定进制保存需要的空间,例如5,使用二进制则为3(101嘛!),使用八进制为1(保存为5即可)char[] buf = new char[chars];//根据计算的空间创建数组formatUnsignedLong(val, shift, buf, 0, chars);return new String(buf, true);}/** * val 待格式化的值 * shift 格式化移位数,4表示16进制,3表示八进制,1表示二进制 * buf 即刚刚创建的字符数组 * offset 开始保存字符的起始索引(都是从0开始写) * len 需要写的字符数(即上面的chars) */static int formatUnsignedLong(long val, int shift, char[] buf, int offset, int len) {int charPos = len;int radix = 1 << shift;//通过shift计算radix,1→2,3→8,4→16int mask = radix - 1;//2进制→1,8进制→0111,16进制→1111do {//很棒的技巧!buf[offset + --charPos] = Integer.digits[((int) val) & mask];//通过与mask进行&操作迅速的通过digits[]获取目标字符val >>>= shift;} while (val != 0 && charPos > 0);return charPos;}/** * Integer.digits */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'}; 

文章来源:智云一二三科技

文章标题:源码阅读Long–JAVA成长之路

文章地址:https://www.zhihuclub.com/196344.shtml

关于作者: 智云科技

热门文章

网站地图