此次來看看Long
的源代碼,基於 jdk1.8.0_181.jdk 版本,若有錯誤,歡迎聯繫指出。html
Long
是long
基礎數據類型的包裝類,從源碼的角度來看,總體的思路和Integer
是極其類似的,若是沒有看過Integer
的源代碼,建議能夠看看 Java源碼 - Integer 這篇文章,相關的思路已經說明清楚。這篇文章類似邏輯會引用原有的邏輯說明,不會再重複介紹了。java
public final class Long extends Number implements Comparable<Long> 複製代碼
帶有final
標識,也就是說不可繼承的。另外繼承了Number
類,而Number
類實現了Serializable
接口,因此Long
也是能夠序列化的;實現了Comparable
接口。git
@Native public static final long MIN_VALUE = 0x8000000000000000L;
@Native public static final long MAX_VALUE = 0x7fffffffffffffffL;
複製代碼
MIN_VALUE
表示了Long
最小值,對應爲-2^63
MAX_VALUE
表示了Lone
最大值,對應爲2^63 - 1
這裏的兩個常量都帶有@Native
註解,表示這兩個常量值字段能夠被native代碼引用。當native代碼和Java代碼都須要維護相同的變量時,若是Java代碼使用了@Native
標記常量字段時,編譯時能夠生成對應的native代碼的頭文件。算法
public static final Class<Long> TYPE = (Class<Long>) Class.getPrimitiveClass("long");
複製代碼
獲取類信息,Long.TYPE == long.class
二者是等價的數組
@Native public static final int SIZE = 64;
複製代碼
表示了Long
的bit數,64位。緩存
public static final int BYTES = SIZE / Byte.SIZE;
複製代碼
表示了Long
的字節數,計算值固定爲8oracle
private final long value;
複製代碼
Long
是long
的包裝類,這裏存放了long
類型對應的數據值信息ide
@Native private static final long serialVersionUID = 4290774380558885855L;
複製代碼
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);
}
}
複製代碼
LongCache
是Long
的靜態內部類,內部定義了一個長度爲128+127+1=256
的數組,用於緩存經常使用的數字範圍,避免後續使用時從新進行實例化,提高性能。函數
public Long(long value) {
this.value = value;
}
public Long(String s) throws NumberFormatException {
this.value = parseLong(s, 10);
}
複製代碼
存在兩個構造函數,支持傳入long
和String
類型參數。但參數類型爲String
時,內部以十進制格式調用了parseLong
方法進行處理實現。post
public static long parseLong(String s) throws NumberFormatException public static long parseLong(String s, int radix) throws NumberFormatException 複製代碼
存在兩個parseLong
方法,具體邏輯和Integer
的parseInt
相似,能夠參考相關的文章介紹parseInt 方法
public static long parseUnsignedLong(String s) throws NumberFormatException {
return parseUnsignedLong(s, 10);
}
public static long parseUnsignedLong(String s, int radix) throws NumberFormatException 複製代碼
存在兩個parseUnsignedLong
方法,第一個方法內部默認以十進制調用了第二個方法進行實現,方法的總體邏輯與Integer
的parseUnsignedInt
基本一致,能夠參考 parseUnsignedInt 方法
static void getChars(long i, int index, char[] buf) 複製代碼
該方法主要的邏輯就是將輸入long
類型數據轉換成字符形式放入char數組中,不支持 Long.MIN_VALUE。具體邏輯說明能夠參考Integer
的getChars 方法
static int stringSize(long x) {
long p = 10;
for (int i=1; i<19; i++) {
if (x < p)
return i;
p = 10*p;
}
return 19;
}
複製代碼
獲取對應數據的字符串長度,設定最大長度19進行循環判斷(由於Long.MAX_VALUE
轉換成十進制數,最大長度爲19位);從邏輯能夠看出,只支持正數,負數的結果是錯誤的。
public String toString() {
return toString(value);
}
public static String toString(long i) public static String toString(long i, int radix) 複製代碼
存在三個toString
方法,其中兩個爲靜態方法,與Integer
一致,能夠參考toString 方法
Integer
的toUnsignedString
方法內部實現調用了Long
的toUnsignedString
方法進行實現,因此咱們來看看這個方法的實現。
public static String toUnsignedString(long i, int radix) {
if (i >= 0)
return toString(i, radix);
else {
switch (radix) {
case 2:
return toBinaryString(i);
case 4:
return toUnsignedString0(i, 2);
case 8:
return toOctalString(i);
case 10:
/* * We can get the effect of an unsigned division by 10 * on a long value by first shifting right, yielding a * positive value, and then dividing by 5. This * allows the last digit and preceding digits to be * isolated more quickly than by an initial conversion * to BigInteger. */
long quot = (i >>> 1) / 5;
long rem = i - quot * 10;
return toString(quot) + rem;
case 16:
return toHexString(i);
case 32:
return toUnsignedString0(i, 5);
default:
return toUnsignedBigInteger(i).toString(radix);
}
}
}
複製代碼
把long
類型數值轉換成對應的無符號字符串。由於long
的最大值爲2^63 - 1
,正數的值轉換成無符號數時依舊在這個範圍內,因此直接調用toString
方法實現。對於負數而言,根據進制的不同,調用不一樣的方法進行處理。
public static String toHexString(long i) {
return toUnsignedString0(i, 4);
}
public static String toOctalString(long i) {
return toUnsignedString0(i, 3);
}
public static String toBinaryString(long i) {
return toUnsignedString0(i, 1);
}
複製代碼
這三個方法的內部實現均是調用了toUnsignedString0
方法,咱們來看下toUnsignedString0
的代碼實現
static String toUnsignedString0(long val, int shift) {
// assert shift > 0 && shift <=5 : "Illegal shift value";
int mag = Long.SIZE - Long.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
formatUnsignedLong(val, shift, buf, 0, chars);
return new String(buf, true);
}
複製代碼
咱們來分析一下這幾行代碼:
int mag = Long.SIZE - Long.numberOfLeadingZeros(val);
複製代碼
mag
表示了該數字表示爲二進制實際須要的位數(去除高位的0)。numberOfLeadingZeros
方法就是獲取該數字二進制格式下最高位1前面的0的個數。
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
複製代碼
根據mag
和shift
獲取轉換成對應進制的字符串所需的字符數(shift
表明了進制數)。
char[] buf = new char[chars];
formatUnsignedLong(val, shift, buf, 0, chars);
複製代碼
將轉換的字符寫入到buf
數組中,來看下formatUnsignedLong
方法實現。
static int formatUnsignedLong(long val, int shift, char[] buf, int offset, int len) {
int charPos = len;
// 獲取進制
int radix = 1 << shift;
// 掩碼,二進制都爲1
int mask = radix - 1;
do {
// 去除對應進制範圍內的最後一位數,獲取對應的字符
buf[offset + --charPos] = Integer.digits[((int) val) & mask];
// 無符號右移,去除上一步已經處理的值
val >>>= shift;
} while (val != 0 && charPos > 0);
return charPos;
}
複製代碼
總體思路就是這樣,這樣就獲取到了對應的字符數組,而後轉換成字符串輸出結果。
private static BigInteger toUnsignedBigInteger(long i) {
if (i >= 0L)
return BigInteger.valueOf(i);
else {
int upper = (int) (i >>> 32);
int lower = (int) i;
// return (upper << 32) + lower
return (BigInteger.valueOf(Integer.toUnsignedLong(upper))).shiftLeft(32).
add(BigInteger.valueOf(Integer.toUnsignedLong(lower)));
}
}
複製代碼
將long
類型值轉換成BigInteger
類型值,當參數>=0
時,使用BigInteger.valueOf(i)
方法處理返回結果;若是小於0,則先拆分按照高位4字節和低位4字節進行處理,而後獲取結果。
public static Long valueOf(String s, int radix) throws NumberFormatException {
return Long.valueOf(parseLong(s, radix));
}
public static Long valueOf(String s) throws NumberFormatException {
return Long.valueOf(parseLong(s, 10));
}
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
複製代碼
存在三個valueOf
方法,前兩個內部實現均是調用了第三個方法。當參數在-128~127
之間時,從LongCache
的緩存數組中獲取,不然從新實例化對象返回。因此能夠看看下面的示例:
Long a = Long.valueOf(108);
Long b = Long.valueOf(108);
Long c = Long.valueOf(1108);
Long d = Long.valueOf(1108);
System.out.println(a == b); // true
System.out.println(c == d); // false
複製代碼
public static Long decode(String nm) throws NumberFormatException 複製代碼
將對應的字符串轉換成整數,支持十進制,0x, 0X, #
開頭的十六進制數,0
開頭的八進制數。代碼邏輯解釋能夠參考 Integer 的 decode 方法
public static Long getLong(String nm) {
return getLong(nm, null);
}
public static Long getLong(String nm, long val) {
Long result = Long.getLong(nm, null);
return (result == null) ? Long.valueOf(val) : result;
}
public static Long getLong(String nm, Long val) {
String v = null;
try {
v = System.getProperty(nm);
} catch (IllegalArgumentException | NullPointerException e) {
}
if (v != null) {
try {
return Long.decode(v);
} catch (NumberFormatException e) {
}
}
return val;
}
複製代碼
三個getLong
方法,主要是最後一個方法。傳入一個配置的key以及默認值,獲取對應的系統配置值,若爲空或者爲null,返回對應的默認值
public byte byteValue() {
return (byte)value;
}
public short shortValue() {
return (short)value;
}
public int intValue() {
return (int)value;
}
public long longValue() {
return value;
}
public float floatValue() {
return (float)value;
}
public double doubleValue() {
return (double)value;
}
複製代碼
直接進行強制類型轉換,返回對應的結果
public int hashCode() {
return Long.hashCode(value);
}
public static int hashCode(long value) {
return (int)(value ^ (value >>> 32));
}
複製代碼
hashCode
返回int
類型結果,首先將輸入參數無符號右移32位,而後與原來的值進行異或運算,強制轉換成int
類型返回對應的結果。
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
複製代碼
首先判斷是否是Long
對象的實例,而後比較二者的值是否相等;這裏能夠看出咱們無需擔憂輸入參數爲null
的時候會出現報錯的狀況。
public static int compare(long x, long y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
複製代碼
比較兩個參數的值,三元表達式,x < y
時返回-1
,x == y
時返回0
,x > y
時返回1
public int compareTo(Long anotherLong) {
return compare(this.value, anotherLong.value);
}
複製代碼
調用compare
方法比較兩個值,返回結果與compare
方法一致
public static int compareUnsigned(long x, long y) {
return compare(x + MIN_VALUE, y + MIN_VALUE);
}
複製代碼
兩個輸入參數看成無符號整數進行比較,這裏經過加上MIN_VALUE
確保在範圍內。有個小技巧,-1加上MIN_VALUE
後會變成最大正數。
public static long divideUnsigned(long dividend, long divisor) {
if (divisor < 0L) { // signed comparison
// Answer must be 0 or 1 depending on relative magnitude
// of dividend and divisor.
return (compareUnsigned(dividend, divisor)) < 0 ? 0L :1L;
}
if (dividend > 0) // Both inputs non-negative
return dividend/divisor;
else {
/* * For simple code, leveraging BigInteger. Longer and faster * code written directly in terms of operations on longs is * possible; see "Hacker's Delight" for divide and remainder * algorithms. */
return toUnsignedBigInteger(dividend).
divide(toUnsignedBigInteger(divisor)).longValue();
}
}
複製代碼
無符號除法,輸入參數轉換成無符號數相除獲取結果
compareUnsigned
比較除數和被除數,被除數 < 除數時返回0,不然返回1(取決於被除數的正負性)toUnsignedBigInteger
轉換成無符號BigInteger
進行運算獲取對應的結果public static long remainderUnsigned(long dividend, long divisor) {
if (dividend > 0 && divisor > 0) { // signed comparisons
return dividend % divisor;
} else {
if (compareUnsigned(dividend, divisor) < 0) // Avoid explicit check for 0 divisor
return dividend;
else
return toUnsignedBigInteger(dividend).
remainder(toUnsignedBigInteger(divisor)).longValue();
}
}
複製代碼
無符號取餘,輸入參數轉換成無符號數進行取餘獲取結果。
toUnsignedBigInteger
方法,二者均轉換成無符號BigInteger
進行運算獲取對應的結果public static long highestOneBit(long i) {
// HD, Figure 3-1
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
i |= (i >> 32);
return i - (i >>> 1);
}
複製代碼
獲取最高位爲1,其他爲0的long
類型值。經過位移或邏輯,將最高位右邊1位設爲1,而後2倍增加左移或操做,1的位數不斷增長,最後1+2+4+8+16+32=63,能夠確保覆蓋全部可能性。而後使用i - (i >>> 1)
保留最高位1返回結果。
public static long lowestOneBit(long i) {
// HD, Section 2-1
return i & -i;
}
複製代碼
獲取最低位1,其他位爲0的值。負數以正數的補碼錶示,對整數的二進制進行取反碼而後加1,獲得的結果與輸入二進制進行與操做,結果就是最低位1保留,其餘位爲0。
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;
}
複製代碼
判斷二進制格式下,最高位的1左邊存在多少個0。這裏使用了相似二分查找的思想,經過左右移位的操做一步步縮小1所在的bit位置範圍,最後經過簡單計算獲取0的個數。開始增長了對特殊值0的判斷。能夠查看numberOfLeadingZeros 方法中的例子加深瞭解
public static int numberOfTrailingZeros(long i) {
// HD, Figure 5-14
int 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);
}
複製代碼
與上面的numberOfLeadingZeros
方法對應,獲取二進制格式下尾部的0的個數。
public static int bitCount(long i) {
// HD, Figure 5-14
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;
}
複製代碼
統計二進制格式下1的數量。代碼第一眼看着是懵的,都是位運算,實際裏面實現的算法邏輯仍是很巧妙的,着實佩服,這裏就不介紹了,感興趣的能夠看下Integer 的 bitCount 方法介紹,瞭解其中的運算邏輯
public static long rotateLeft(long i, int distance) {
return (i << distance) | (i >>> -distance);
}
public static long rotateRight(long i, int distance) {
return (i >>> distance) | (i << -distance);
}
複製代碼
旋轉二進制,rotateLeft
將特定位數的高位bit放置低位,返回對應的數值;rotateRight
將特定位數的低位bit放置高位,返回對應的數值。當distance
是負數的時候,rotateLeft(val, -distance) == rotateRight(val, distance)
以及rotateRight(val, -distance) == rotateLeft(val, distance)
。另外,當distance
是64的任意倍數時,實際是沒有效果的 ,至關於無操做。
這裏須要說一下(i >>> -distance)
,diatance
爲正數時,右移一個負數邏輯至關於i >>> 64+(-distance)
public static long reverse(long i) {
// HD, Figure 7-1
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;
}
複製代碼
看着是否是和bitCount
有點相似,其實核心邏輯類似。先是交換相鄰1位bit的順序,再交換相鄰2位bit順序,再繼續交換相鄰4位bit順序,而後交換相鄰8位bit順序,再交換中間32位bit的順序,最後最高16位再和低16位進行交換就完成了整個過程,全程位運算甚是奇妙。
public static int signum(long i) {
// HD, Section 2-7
return (int) ((i >> 63) | (-i >>> 63));
}
複製代碼
獲取符號數,若爲負數,返回-1;若爲0則返回0;爲正數,則返回1。
public static long reverseBytes(long i) {
i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
return (i << 48) | ((i & 0xffff0000L) << 16) |
((i >>> 16) & 0xffff0000L) | (i >>> 48);
}
複製代碼
按照輸入參數二進制格式,以字節爲單位進行翻轉,返回對應的結果數值。
(i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL
交換相鄰字節順序(i >>> 48) | (i << 48)
交換最高16位和最低16位bit位置。((i >>> 16) & 0xffff0000L) | ((i & 0xffff0000L) << 16)
交換中間32位bit位置public static long sum(long a, long b) {
return a + b;
}
public static long max(long a, long b) {
return Math.max(a, b);
}
public static long min(long a, long b) {
return Math.min(a, b);
}
複製代碼
這個就不說了,很簡單的方法。
由於long
是64bit,須要注意下long
的原子性邏輯,這裏是官方文檔的具體說明Non-Atomic Treatment of double
and long
,相關解釋說明能夠參考 double文章中的詳細說明。
總的代碼邏輯來看,Long
和Integer
的方法基本一致,方法內的實現也基本一致,若是瞭解了Integer
的相關邏輯,Long
相關代碼能夠快速的過一下便可;另外值得注意的是long
的原子性問題。