1. 程式人生 > >Hadoop Serialization -- hadoop序列化具體解釋 (2)【Text,BytesWritable,NullWritable】

Hadoop Serialization -- hadoop序列化具體解釋 (2)【Text,BytesWritable,NullWritable】

tao small oid rem cef get() 每一個 包含 協同工作

回想: 回想序列化,事實上原書的結構非常清晰,我截圖給出書中的章節結構: 技術分享 序列化最基本的,最底層的是實現writable接口,wiritable規定讀和寫的遊戲規則 (void write(DataOutput out) throws IOException; void readFields(DataInput in) throws IOException;)。為了適應hadoop的mapreduce的運算特性,也就是map 和reduce對key的比較,排序的功能,就要實現Comparable接口,這個接口規定 public int compareTo(T o);這種方法。為了增強處理大數據集的能力。我們不能老是先序列化,傳輸,反序列化。然後進行比較compare,太消耗時間和性能了。我們有了增強的RawComparator,RawComparator是Comparator的增強版,能夠比較沒有被反序列化的數據。

hadoop須要處理的數據五花八門,java具有的基本數據類型都有可能在hadoop中出現,hadoop因此包裝了java的基本數據類型使他們實現以上的接口而且給予實現細節。這些類都實現了WritableComparable接口。插上飛翔的翅膀,能夠在不同的hadoop節點之間毫無障礙的傳輸了。如入無人之境。


技術分享
既然Text拿出來單獨討論。

自然就要好好研究一下Text的實現細節,對於我們對hadoop的設計細節和思想太重要太重要。


Text是UTF-8字符串的Writable實現。被看做是java String類型的替換。Text 類取代了UTF8 類, UTF8 類不支持

編碼大於32767 個字節的字符.使用了Java 改進過的UTF-8.Text 使用int 型(使用一個可變長度的編碼方案)在字符感編碼中存儲字節數. 最大值是2 GB 。此外。 Text 使用標準的UTF芯,使其更易於與理解U T F-8 的其它工具協同工作.

為什麽是2GB,我預計非常少人會思考這個問題,我們簡單計算一下:
利用int存儲字節長度,int最大是2^31-1,那麽字節最大長度就是2^31-1
Text可以容納的大小R=(2^31-1)/1024/1024/1024=1.99999999=2GB 因此我們使用他的時候要知道他的大小是有限制的。
因為強調使用標準的UTF8,所以Text 和Java 的String 類之間還是有一些差別的。Text 類的索引位於編碼後的字節系列中,而不是字符串中的Unicode 字符.或Java 的char 編碼單元{如同String 一樣)。舉比例如以下:

這方面的差異用中文就非常好的說明這個問題。

String line = "滾滾長江東逝水"; System.out.println(line.length()); Text text = new Text(line); System.out.println(text.getLength()); System.out.println(line.charAt(2)); System.out.println(text.charAt(2));
輸出: 7 21
-1 String line = "merry christmas"; System.out.println(line.length()); Text text = new Text(line); System.out.println(text.getLength()); System.out.println(line.charAt(2)); System.out.println(text.charAt(2));
輸出: 15 15 r 114 能夠看出來,他們的索引(Index)是真的不一樣。同一個索引值取出來的並非同一個東西。


註意, charAt ( )返回了一個int 類型來表示Unicode 代碼點, 而不是像String 變量那樣返回一個char 類型。

在開始使用一個以上字節進行編碼的字符(比如中文。!

), Text 和String 之間的差別是非常明顯的。

下表展示了Unicode的代碼點。
技術分享 U+0041 代碼點相應大寫字母A 一直到U+00DFUTF-8都是一個字節編碼。剩下的都是兩個字節以上。而對於java,最後一行,僅僅有最後一個代碼點是兩個。其它的都是一個字節的。這點區別非常大。
怕非常多人不懂代碼點,我再解釋一下: Unicode 是通用字符編碼標準。用於表示文本以供計算機處理。Unicode 提供了一種對多語種文本進行一致編碼的方法,便於國際文本文件的交換。每一個 Unicode 字符均映射到一個代碼點,代碼點是一個介於 0 和 1,114,111 之間的整數。Unicode 代碼點使用 U+nnnn 形式的表示法來表示(當中 nnnn 是代碼點的十六進制數),或使用描寫敘述代碼點的文本字符串來表示。比如,小寫字母 “a” 能夠用 U+0061 或文本字符串 "LATIN SMALL LETTER A" 來表示。 代碼點能夠使用不同的字符編碼方案進行編碼。

在 Oracle Solaris Unicode 語言環境中,使用的是 UTF-8 形式。UTF-8 是 Unicode 的一種可變長度編碼形式,它透明地保留了 ASCII 字符代碼值(請參見UTF-8 概述)。 代碼點就是一個字符在Unicode中相應的編碼。

String 的長度是它包含的字符個數 。但Text 對象的長度是其UTF -8 編碼的字節數. 相同。 indexOf () 方泣返回一個char 類型的編碼單元的索引。find () 方格是字節偏移量.請看樣例:

@Test public void string() throws UnsupportedEncodingException { String s = "\u0041\u00DF\u6771\uD801\uDC00"; assertThat(s.length(), is(5)); assertThat(s.getBytes("UTF-8").length, is(10)); assertThat(s.indexOf("\u0041"), is(0)); assertThat(s.indexOf("\u00DF"), is(1)); assertThat(s.indexOf("\u6771"), is(2)); assertThat(s.indexOf("\uD801\uDC00"), is(3)); assertThat(s.charAt(0), is(‘\u0041‘)); assertThat(s.charAt(1), is(‘\u00DF‘)); assertThat(s.charAt(2), is(‘\u6771‘)); assertThat(s.charAt(3), is(‘\uD801‘)); assertThat(s.charAt(4), is(‘\uDC00‘)); assertThat(s.codePointAt(0), is(0x0041)); assertThat(s.codePointAt(1), is(0x00DF)); assertThat(s.codePointAt(2), is(0x6771)); assertThat(s.codePointAt(3), is(0x10400)); }
@Test public void text() { Text t = new Text("\u0041\u00DF\u6771\uD801\uDC00"); assertThat(t.getLength(), is(10)); //10 = 1+2+3+4 是其UTF -8 編碼的字節數 assertThat(t.find("\u0041"), is(0)); assertThat(t.find("\u00DF"), is(1)); assertThat(t.find("\u6771"), is(3)); assertThat(t.find("\uD801\uDC00"), is(6)); assertThat(t.charAt(0), is(0x0041)); assertThat(t.charAt(1), is(0x00DF)); assertThat(t.charAt(3), is(0x6771)); assertThat(t.charAt(6), is(0x10400)); }
遍歷Text,叠代 叠代使用索引的字節偏移對Text 中的Unicode 字符進行途代是非常復雜的,由於你不能僅僅添加索引。叠代的定義有點模糊(見例4-6 ) 將Text 對象變成java.nio.ByteBuffer然後對緩沖的Text 重復調用bytesToCodePoint() 靜態方法.這個方泣提取下一個代碼點作為int 然後更新緩沖中的位置。當bytesToCodePoint() 返回- 1 時,檢測到字符結束。意思就是說,我們取字符的時候。是一整個一整個字符的取,我們不可以依照索引來取,我們依照代碼點整個整個的取。



public class TextIterator { public static void main(String[] args) { Text t = new Text("\u0041\u00DF\u6771\uD801\uDC00"); ByteBuffer buf = ByteBuffer.wrap(t.getBytes(), 0, t.getLength()); int cp; while (buf.hasRemaining() && (cp = Text.bytesToCodePoint(buf)) != -1) { System.out.println(Integer.toHexString(cp)); } } } 輸出: 41 df 6771 10400
可改動性 String 和Text 的還有一個差別在於可改動性(像Hadoop 中的全部Writable 實視一樣。但NullWritable 除外,後者是單實例對象)。

我們能夠通過對它調用set() 函數來重用Text 實例。

示比例如以下: Text t = new Text("hadoop"); t.set("pig"); assertThat(t.getLength(), is(3)); assertThat(t.getBytes().length, is(3));
轉為字符串 Text 不像java. l ang.String 一樣有一個能夠處理字符串的API ,所以在很多情況下,須要將Text 對象轉化為String 對象。這通經常使用toString()方法來完畢。

assertThat(new Text("hadoop ") . toString() , is( "hadoop"));

BytesWritable
BytesWritable 是一個二進制數據數組封裝。

它的序列化格式是一個int 字段(4字節) ,指定的是字節數及字節本身。

比如。 一個長度為2 ,值為3 和5 的字節數組序列化為一個4 字節的整數(00000002)加上兩個來自數組的字節(03 和05) 。
BytesWritable b = new BytesWritable(new byte[] { 3, 5 }); byte[] bytes = serialize(b); assertThat(StringUtils.byteToHexString(bytes), is("000000020305"));
BytesWritab1e 是可變的。其值可通過調用set ( )方撞來改變。和Text一樣 。從getBytes ( )方法返回的字節數組大小可能並沒有反映出存儲在BytesWritable 的數據的實際大小.能夠通過調用getLength () 方法來確定BytesWritable 的長度。比如:
b.setCapacity(11); assertThat(b.getLength(), is(2)); assertThat(b.getBytes().length, is(11));
NullWritable NullWritable 是一種特殊的Writable 類型,由於它的序列化是零長度的。

沒有字節被寫入流或從流中讀出.它被用作占位符.比如,在MapReduce 中,在不需要這個位置的時候,鍵或值能夠被聲明為NullWritable,他有效存儲了一個不變的空值。NullWritable 也能夠非常實用,在打算存儲一系列值的時候,作為SequenceFile 的一個鍵,而不是鍵/值對。

它是一個不變的單實例,事實上例能夠通過調用NullWritable.get() 方法來檢索。
今天就到這裏。
Charles 2015-12-24晚於P.P



版權說明: 本文由Charles Dong原創,本人支持開源以及免費故意的傳播。反對商業化謀利。 CSDN博客:http://blog.csdn.net/mrcharles 個人站:http://blog.xingbod.cn EMAIL:[email protected]

Hadoop Serialization -- hadoop序列化具體解釋 (2)【Text,BytesWritable,NullWritable】