對Text型別的資料,使用getLength()與getBytes().length獲取的長度不同解析
我們先來看一個小Demo:
Text t = new Text("hadoop");
t.set("pig");
System.out.println(t.getLength());
System.out.println(t.getBytes().length);
不出意外,輸出的結果都是3。但是這個時候如果我們把set方法中的引數改為如下型別將會發生什麼事呢?
t.set(new Text("pig"));
執行程式,結果卻發生了變化,分別輸出了3和6。這是怎麼回事呢?別急,我們通過原始碼看能否得到想要的回答。我們跟蹤set接收的引數型別為Text的方法,如下:
/** copy a text. */ public void set(Text other) { set(other.getBytes(), 0, other.getLength()); }
繼續跟進set方法,如下:
public void set(byte[] utf8, int start, int len) {
setCapacity(len, false);
System.arraycopy(utf8, start, bytes, 0, len);
this.length = len;
}
我們對當前這個set方法,進行研究一番。首先跟進setCapacity方法:
/* * Sets the capacity of this Text object to <em>at least</em> * <code>len</code> bytes. If the current buffer is longer, * then the capacity and existing content of the buffer are * unchanged. If <code>len</code> is larger * than the current capacity, the Text object's capacity is * increased to match. * @param len the number of bytes we need * @param keepData should the old data be kept */ private void setCapacity(int len, boolean keepData) { if (bytes == null || bytes.length < len) { if (bytes != null && keepData) { bytes = Arrays.copyOf(bytes, Math.max(len,length << 1)); } else { bytes = new byte[len]; } } }
通過方法註釋我們很容易知道,當Text當前的buffer長度大於傳過來的len,這個時候buffer的長度和其儲存的內容都是不會發生改變的。但是,如果len大於當前buffer的長度時候,Text物件的長度就需要進行增加。
所以,當我們先通過Text t = new Text("hadoop");建立Text物件的時候,buffer裡面儲存了hadoop,且長度為6。這個時候,我們再使用t.set(new Text("pig"));改變buffer的值的時候,就會呼叫上述的方法。我們知道buffer的長度大於新設定的Text的長度,這個時候,buffer將不會發生任何改變。
緊接著執行System.arraycopy(utf8, start, bytes, 0, len);這是系統本地方法,目的是將utf8按長度複製到bytes(即上述的buffer)中,其餘長度中的不會發生改變。
比如說,將pig複製到hadoop,結果為pigoop。
此時,再通過this.length = len;更新下Text物件的長度。
我們再來回顧下小Demo中的輸出程式中的關鍵方法:
System.out.println(t.getLength());
System.out.println(t.getBytes().length);
對於t.getLength()我們跟程序序很明瞭的知道他是直接返回Text物件的length,即3。
/** Returns the number of bytes in the byte array */
@Override
public int getLength() {
return length;
}
對於t.getBytes().length,我們是先獲得Text物件的bytes,即pigoop,然後在求出其長度,為6。
但是,問題來了,為什麼使用t.set("pig");的時候長度都是為3呢?很顯然,呼叫的set方法的實現並不相同,我們來看看:
public void set(String string) {
try {
ByteBuffer bb = encode(string, true);
bytes = bb.array();
length = bb.limit();
}catch(CharacterCodingException e) {
throw new RuntimeException("Should not have happened ", e);
}
}
上面程式碼的第一局將制定的string使用utf-8格式轉化為位元組buffer(Converts the provided String to bytes using the UTF-8 encoding.)。然後,將位元組buffer通過array方法轉化為位元組陣列bytes。並通過limit方法返回buffer的長度賦值給length。此時,無論通過t.getLength();還是t.getBytes().length都將返回相同的長度。