[Java源碼]Long

此次來看看Long的源代碼,基於 jdk1.8.0_181.jdk 版本,若有錯誤,歡迎聯繫指出。html

前言

Longlong基礎數據類型的包裝類,從源碼的角度來看,總體的思路和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;
複製代碼

Longlong的包裝類,這裏存放了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);
  }
}
複製代碼

LongCacheLong的靜態內部類,內部定義了一個長度爲128+127+1=256的數組,用於緩存經常使用的數字範圍,避免後續使用時從新進行實例化,提高性能。函數

方法

構造方法

public Long(long value) {
  this.value = value;
}

public Long(String s) throws NumberFormatException {
  this.value = parseLong(s, 10);
}
複製代碼

存在兩個構造函數,支持傳入longString類型參數。但參數類型爲String時,內部以十進制格式調用了parseLong方法進行處理實現。post

parseLong 方法

public static long parseLong(String s) throws NumberFormatException public static long parseLong(String s, int radix) throws NumberFormatException 複製代碼

存在兩個parseLong方法,具體邏輯和IntegerparseInt相似,能夠參考相關的文章介紹parseInt 方法

parseUnsignedLong 方法

public static long parseUnsignedLong(String s) throws NumberFormatException {
  return parseUnsignedLong(s, 10);
}

public static long parseUnsignedLong(String s, int radix) throws NumberFormatException 複製代碼

存在兩個parseUnsignedLong方法,第一個方法內部默認以十進制調用了第二個方法進行實現,方法的總體邏輯與IntegerparseUnsignedInt基本一致,能夠參考 parseUnsignedInt 方法

getChars 方法

static void getChars(long i, int index, char[] buf) 複製代碼

該方法主要的邏輯就是將輸入long類型數據轉換成字符形式放入char數組中,不支持 Long.MIN_VALUE。具體邏輯說明能夠參考IntegergetChars 方法

stringSize 方法

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位);從邏輯能夠看出,只支持正數,負數的結果是錯誤的。

toString 方法

public String toString() {
  return toString(value);
}

public static String toString(long i) public static String toString(long i, int radix) 複製代碼

存在三個toString方法,其中兩個爲靜態方法,與Integer一致,能夠參考toString 方法

toUnsignedString 方法

IntegertoUnsignedString方法內部實現調用了LongtoUnsignedString方法進行實現,因此咱們來看看這個方法的實現。

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);
複製代碼

根據magshift獲取轉換成對應進制的字符串所需的字符數(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;
}
複製代碼

總體思路就是這樣,這樣就獲取到了對應的字符數組,而後轉換成字符串輸出結果。

toUnsignedBigInteger 方法

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字節進行處理,而後獲取結果。

valueOf 方法

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
複製代碼

decode 方法

public static Long decode(String nm) throws NumberFormatException 複製代碼

將對應的字符串轉換成整數,支持十進制,0x, 0X, #開頭的十六進制數,0開頭的八進制數。代碼邏輯解釋能夠參考 Integer 的 decode 方法

getLong 方法

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,返回對應的默認值

xxxValue 方法

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;
}
複製代碼

直接進行強制類型轉換,返回對應的結果

hashCode 方法

public int hashCode() {
  return Long.hashCode(value);
}

public static int hashCode(long value) {
  return (int)(value ^ (value >>> 32));
}
複製代碼

hashCode返回int類型結果,首先將輸入參數無符號右移32位,而後與原來的值進行異或運算,強制轉換成int類型返回對應的結果。

equals 方法

public boolean equals(Object obj) {
  if (obj instanceof Long) {
    return value == ((Long)obj).longValue();
  }
  return false;
}
複製代碼

首先判斷是否是Long對象的實例,而後比較二者的值是否相等;這裏能夠看出咱們無需擔憂輸入參數爲null的時候會出現報錯的狀況。

compare 方法

public static int compare(long x, long y) {
  return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
複製代碼

比較兩個參數的值,三元表達式,x < y 時返回-1x == y 時返回0x > y 時返回1

compareTo 方法

public int compareTo(Long anotherLong) {
  return compare(this.value, anotherLong.value);
}
複製代碼

調用compare方法比較兩個值,返回結果與compare方法一致

compareUnsigned 方法

public static int compareUnsigned(long x, long y) {
  return compare(x + MIN_VALUE, y + MIN_VALUE);
}
複製代碼

兩個輸入參數看成無符號整數進行比較,這裏經過加上MIN_VALUE確保在範圍內。有個小技巧,-1加上MIN_VALUE後會變成最大正數。

divideUnsigned 方法

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();
  }
}
複製代碼

無符號除法,輸入參數轉換成無符號數相除獲取結果

  • 當除數小於0時,調用compareUnsigned比較除數和被除數,被除數 < 除數時返回0,不然返回1(取決於被除數的正負性)
  • 當除數和被除數均爲正數值,直接使用除法運算獲取結果
  • 當被除數小於0,除數大於0時,使用toUnsignedBigInteger轉換成無符號BigInteger進行運算獲取對應的結果

remainderUnsigned 方法

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();
  }
}
複製代碼

無符號取餘,輸入參數轉換成無符號數進行取餘獲取結果。

  • 當除數和被除數均大於0時,直接進行取餘運算獲取結果
  • 當轉換成無符號數,除數小於被除數時,直接返回除數做爲結果
  • 不然使用toUnsignedBigInteger方法,二者均轉換成無符號BigInteger進行運算獲取對應的結果

highestOneBit 方法

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返回結果。

lowestOneBit 方法

public static long lowestOneBit(long i) {
  // HD, Section 2-1
  return i & -i;
}
複製代碼

獲取最低位1,其他位爲0的值。負數以正數的補碼錶示,對整數的二進制進行取反碼而後加1,獲得的結果與輸入二進制進行與操做,結果就是最低位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;
}
複製代碼

判斷二進制格式下,最高位的1左邊存在多少個0。這裏使用了相似二分查找的思想,經過左右移位的操做一步步縮小1所在的bit位置範圍,最後經過簡單計算獲取0的個數。開始增長了對特殊值0的判斷。能夠查看numberOfLeadingZeros 方法中的例子加深瞭解

numberOfTrailingZeros 方法

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的個數。

bitCount 方法

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 方法介紹,瞭解其中的運算邏輯

rotateXXX 方法

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)

reverse 方法

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位進行交換就完成了整個過程,全程位運算甚是奇妙。

signum 方法

public static int signum(long i) {
  // HD, Section 2-7
  return (int) ((i >> 63) | (-i >>> 63));
}
複製代碼

獲取符號數,若爲負數,返回-1;若爲0則返回0;爲正數,則返回1。

reverseBytes 方法

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位置

sum、max、min 方法

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 doubleand long,相關解釋說明能夠參考 double文章中的詳細說明。

總結

總的代碼邏輯來看,LongInteger的方法基本一致,方法內的實現也基本一致,若是瞭解了Integer的相關邏輯,Long相關代碼能夠快速的過一下便可;另外值得注意的是long的原子性問題。

最後

博客地址:blog.renyijiu.com/post/java源碼…

相關文章
相關標籤/搜索