原文鏈接 http://blog.csdn.net/lastsweetop/article/details/9249411
?
所有源碼在github上,https://github.com/lastsweetop/styhadoop
簡介
在hadoop中,Writable的實現(xiàn)類是個龐大的家族,我們在這里簡單的介紹一下常用來做序列化的一部分。
?
java原生類型
除char類型以外,所有的原生類型都有對應(yīng)的Writable類,并且通過get和set方法可以他們的值。 IntWritable和LongWritable還有對應(yīng)的變長VIntWritable和VLongWritable類。 固定長度還是變長的選用類似與數(shù)據(jù)庫中的char或者vchar,在這里就不贅述了。
?
Text類型
Text類型使用變長int型存儲長度,所以Text類型的最大存儲為2G. Text類型采用標(biāo)準(zhǔn)的utf-8編碼,所以與其他文本工具可以非常好的交互,但要注意的是,這樣的話就和java的String類型差別就很多了。
檢索的不同
Text的chatAt返回的是一個整型,及utf-8編碼后的數(shù)字,而不是象String那樣的unicode編碼的char類型。
[java]?view plaincopy @Test??public?void?testTextIndex(){??????Text?text=new?Text("hadoop");??????Assert.assertEquals(text.getLength(),?6);??????Assert.assertEquals(text.getBytes().length,?6);??????Assert.assertEquals(text.charAt(2),(int)'d');??????Assert.assertEquals("Out?of?bounds",text.charAt(100),-1);??}?? Text還有個find方法,類似String里indexOf方法
[java]?view plaincopy @Test??public?void?testTextFind()?{??????Text?text?=?new?Text("hadoop");??????Assert.assertEquals("find?a?substring",text.find("do"),2);??????Assert.assertEquals("Find?first?'o'",text.find("o"),3);??????Assert.assertEquals("Find?'o'?from?position?4?or?later",text.find("o",4),4);??????Assert.assertEquals("No?match",text.find("pig"),-1);??}?? Unicode的不同
當(dāng)uft-8編碼后的字節(jié)大于兩個時,Text和String的區(qū)別就會更清晰,因為String是按照unicode的char計算,而Text是按照字節(jié)計算。 我們來看下1到4個字節(jié)的不同的unicode字符 4個unicode分別占用1到4個字節(jié),u+10400在java的unicode字符重占用兩個char,前三個字符分別占用1個char 我們通過代碼來看下String和Text的不同
[java]?view plaincopy @Test?????public?void?string()?throws?UnsupportedEncodingException?{?????????String?str?=?"\u0041\u00DF\u6771\uD801\uDC00";?????????Assert.assertEquals(str.length(),?5);?????????Assert.assertEquals(str.getBytes("UTF-8").length,?10);???????????Assert.assertEquals(str.indexOf("\u0041"),?0);?????????Assert.assertEquals(str.indexOf("\u00DF"),?1);?????????Assert.assertEquals(str.indexOf("\u6771"),?2);?????????Assert.assertEquals(str.indexOf("\uD801\uDC00"),?3);???????????Assert.assertEquals(str.charAt(0),?'\u0041');?????????Assert.assertEquals(str.charAt(1),?'\u00DF');?????????Assert.assertEquals(str.charAt(2),?'\u6771');?????????Assert.assertEquals(str.charAt(3),?'\uD801');?????????Assert.assertEquals(str.charAt(4),?'\uDC00');???????????Assert.assertEquals(str.codePointAt(0),?0x0041);?????????Assert.assertEquals(str.codePointAt(1),?0x00DF);?????????Assert.assertEquals(str.codePointAt(2),?0x6771);?????????Assert.assertEquals(str.codePointAt(3),?0x10400);?????}???????@Test?????public?void?text()?{?????????Text?text?=?new?Text("\u0041\u00DF\u6771\uD801\uDC00");?????????Assert.assertEquals(text.getLength(),?10);???????????Assert.assertEquals(text.find("\u0041"),?0);?????????Assert.assertEquals(text.find("\u00DF"),?1);?????????Assert.assertEquals(text.find("\u6771"),?3);?????????Assert.assertEquals(text.find("\uD801\uDC00"),?6);???????????Assert.assertEquals(text.charAt(0),?0x0041);?????????Assert.assertEquals(text.charAt(1),?0x00DF);?????????Assert.assertEquals(text.charAt(3),?0x6771);?????????Assert.assertEquals(text.charAt(6),?0x10400);?????}?? 這樣一比較就很明顯了。 1.String的length()方法返回的是char的數(shù)量,Text的getLength()方法返回的是字節(jié)的數(shù)量。 2.String的indexOf()方法返回的是以char為單元的偏移量,Text的find()方法返回的是以字節(jié)為單位的偏移量。 3.String的charAt()方法不是返回的整個unicode字符,而是返回的是java中的char字符 4.String的codePointAt()和Text的charAt方法比較類似,不過要注意,前者是按char的偏移量,后者是字節(jié)的偏移量
Text的迭代
在Text中對unicode字符的迭代是相當(dāng)復(fù)雜的,因為與unicode所占的字節(jié)數(shù)有關(guān),不能簡單的使用index的增長來確定。首先要把Text對象使用ByteBuffer進(jìn)行封裝,然后再調(diào)用Text的靜態(tài)方法bytesToCodePoint對ByteBuffer進(jìn)行輪詢返回unicode字符的code point。看一下示例代碼:
[java]?view plaincopy package?com.sweetop.styhadoop;????import?org.apache.hadoop.io.Text;????import?java.nio.ByteBuffer;????public?class?TextIterator?{??????public?static?void?main(String[]?args)?{??????????Text?text?=?new?Text("\u0041\u00DF\u6771\uD801\udc00");??????????ByteBuffer?buffer?=?ByteBuffer.wrap(text.getBytes(),?0,?text.getLength());??????????int?cp;??????????while?(buffer.hasRemaining()?&&?(cp?=?Text.bytesToCodePoint(buffer))?!=?-1)?{??????????????System.out.println(Integer.toHexString(cp));??????????}??????}??}?? Text的修改
除了NullWritable是不可更改外,其他類型的Writable都是可以修改的。你可以通過Text的set方法去修改去修改重用這個實例。
[java]?view plaincopy @Test??public?void?testTextMutability()?{??????Text?text?=?new?Text("hadoop");??????text.set("pig");??????Assert.assertEquals(text.getLength(),?3);??????Assert.assertEquals(text.getBytes().length,?3);??}?? 但要注意的就是,在某些情況下Text的getBytes方法返回的字節(jié)數(shù)組的長度和Text的getLength方法返回的長度不一致。因此,在調(diào)用getBytes()方法的同時最好也調(diào)用一下getLength方法,這樣你就知道在字節(jié)數(shù)組里有多少有效的字符。
[java]?view plaincopy @Test??public?void?testTextMutability2()?{??????Text?text?=?new?Text("hadoop");??????text.set(new?Text("pig"));??????Assert.assertEquals(text.getLength(),3);??????Assert.assertEquals(text.getBytes().length,6);??}?? BytesWritable類型
ByteWritable類型是一個二進(jìn)制數(shù)組的封裝類型,序列化格式是以一個4字節(jié)的整數(shù)(這點與Text不同,Text是以變長int開頭)開始表明字節(jié)數(shù)組的長度,然后接下來就是數(shù)組本身。看下示例:
[java]?view plaincopy @Test??public?void?testByteWritableSerilizedFromat()?throws?IOException?{??????BytesWritable?bytesWritable=new?BytesWritable(new?byte[]{3,5});??????byte[]?bytes=SerializeUtils.serialize(bytesWritable);??????Assert.assertEquals(StringUtils.byteToHexString(bytes),"000000020305");??}?? 和Text一樣,ByteWritable也可以通過set方法修改,getLength返回的大小是真實大小,而getBytes返回的大小確不是。
[java]?view plaincopy <span?style="white-space:pre">??</span>bytesWritable.setCapacity(11);??????????bytesWritable.setSize(4);??????????Assert.assertEquals(4,bytesWritable.getLength());??????????Assert.assertEquals(11,bytesWritable.getBytes().length);?? NullWritable類型
NullWritable是一個非常特殊的Writable類型,序列化不包含任何字符,僅僅相當(dāng)于個占位符。你在使用mapreduce時,key或者value在無需使用時,可以定義為NullWritable。
[java]?view plaincopy package?com.sweetop.styhadoop;????import?org.apache.hadoop.io.NullWritable;??import?org.apache.hadoop.util.StringUtils;????import?java.io.IOException;????public?class?TestNullWritable?{??????public?static?void?main(String[]?args)?throws?IOException?{??????????NullWritable?nullWritable=NullWritable.get();??????????System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(nullWritable)));??????}??}?? ObjectWritable類型
ObjectWritable是其他類型的封裝類,包括java原生類型,String,enum,Writable,null等,或者這些類型構(gòu)成的數(shù)組。當(dāng)你的一個field有多種類型時,ObjectWritable類型的用處就發(fā)揮出來了,不過有個不好的地方就是占用的空間太大,即使你存一個字母,因為它需要保存封裝前的類型,我們來看瞎示例:
[java]?view plaincopy package?com.sweetop.styhadoop;????import?org.apache.hadoop.io.ObjectWritable;??import?org.apache.hadoop.io.Text;??import?org.apache.hadoop.util.StringUtils;????import?java.io.IOException;????public?class?TestObjectWritable?{??????public?static?void?main(String[]?args)?throws?IOException?{??????????Text?text=new?Text("\u0041");??????????ObjectWritable?objectWritable=new?ObjectWritable(text);??????????System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(objectWritable)));????????}??}?? 僅僅是保存一個字母,那么看下它序列化后的結(jié)果是什么:
[java]?view plaincopy 00196f72672e6170616368652e6861646f6f702e696f2e5465787400196f72672e6170616368652e6861646f6f702e696f2e546578740141?? 太浪費(fèi)空間了,而且類型一般是已知的,也就那么幾個,那么它的代替方法出現(xiàn),看下一小節(jié)
GenericWritable類型
使用GenericWritable時,只需繼承于他,并通過重寫getTypes方法指定哪些類型需要支持即可,我們看下用法:
[java]?view plaincopy package?com.sweetop.styhadoop;????import?org.apache.hadoop.io.GenericWritable;??import?org.apache.hadoop.io.Text;??import?org.apache.hadoop.io.Writable;????class?MyWritable?extends?GenericWritable?{????????MyWritable(Writable?writable)?{??????????set(writable);??????}????????public?static?Class<??extends?Writable>[]?CLASSES=null;????????static?{??????????CLASSES=??(Class<??extends?Writable>[])new?Class[]{??????????????????Text.class??????????};??????}????????@Override??????protected?Class<??extends?Writable>[]?getTypes()?{??????????return?CLASSES;??????}??}?? 然后輸出序列化后的結(jié)果
[java]?view plaincopy package?com.sweetop.styhadoop;????import?org.apache.hadoop.io.IntWritable;??import?org.apache.hadoop.io.Text;??import?org.apache.hadoop.io.VIntWritable;??import?org.apache.hadoop.util.StringUtils;????import?java.io.IOException;????public?class?TestGenericWritable?{????????public?static?void?main(String[]?args)?throws?IOException?{??????????Text?text=new?Text("\u0041\u0071");??????????MyWritable?myWritable=new?MyWritable(text);??????????System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(text)));??????????System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(myWritable)));????????}??}?? 結(jié)果是:
[java]?view plaincopy 024171??00024171?? GenericWritable的序列化只是把類型在type數(shù)組里的索引放在了前面,這樣就比ObjectWritable節(jié)省了很多空間,所以推薦大家使用GenericWritable
集合類型的Writable
ArrayWritable和TwoDArrayWritable
ArrayWritable和TwoDArrayWritable分別表示數(shù)組和二維數(shù)組的Writable類型,指定數(shù)組的類型有兩種方法,構(gòu)造方法里設(shè)置,或者繼承于ArrayWritable,TwoDArrayWritable也是一樣。
[java]?view plaincopy package?com.sweetop.styhadoop;????import?org.apache.hadoop.io.ArrayWritable;??import?org.apache.hadoop.io.Text;??import?org.apache.hadoop.io.Writable;??import?org.apache.hadoop.util.StringUtils;????import?java.io.IOException;????public?class?TestArrayWritable?{??????public?static?void?main(String[]?args)?throws?IOException?{??????????ArrayWritable?arrayWritable=new?ArrayWritable(Text.class);??????????arrayWritable.set(new?Writable[]{new?Text("\u0071"),new?Text("\u0041")});??????????System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(arrayWritable)));??????}??}?? 看下輸出:
[java]?view plaincopy 0000000201710141?? 可知,ArrayWritable以一個整型開始表示數(shù)組長度,然后數(shù)組里的元素一一排開。 ArrayPrimitiveWritable和上面類似,只是不需要用子類去繼承ArrayWritable而已。
MapWritable和SortedMapWritable
MapWritable對應(yīng)Map,SortedMapWritable對應(yīng)SortedMap,以4個字節(jié)開頭,存儲集合大小,然后每個元素以一個字節(jié)開頭存儲類型的索引(類似GenericWritable,所以總共的類型總數(shù)只能倒127),接著是元素本身,先key后value,這樣一對對排開。 這兩個Writable以后會用很多,貫穿整個hadoop,這里就不寫示例了。 我們注意到?jīng)]看到set集合和list集合,這個可以代替實現(xiàn)。用MapWritable代替set,SortedMapWritable代替sortedmap,只需將他們的values設(shè)置成NullWritable即可,NullWritable不占空間。相同類型構(gòu)成的list,可以用ArrayWritable代替,不同類型的list可以用GenericWritable實現(xiàn)類型,然后再使用ArrayWritable封裝。當(dāng)然MapWritable一樣可以實現(xiàn)list,把key設(shè)置為索引,values做list里的元素。
轉(zhuǎn)載于:https://www.cnblogs.com/ihongyan/p/5137275.html
總結(jié)
以上是生活随笔為你收集整理的【转】hadoop深入研究:(十一)——序列化与Writable实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。