1.BytesWritable
<1>定義
ByteWritable是對二進制數據組的封裝。它的序列化格式為一個用於指定后面數據字節數的整數域(4個字節),后跟字節本身。
舉個例子,假如有一個數組bytes,里面有兩個byte,bytes[0]=3,bytes[1]=5,那么,數組序列化后,其返回一個字節數組,序列化方面,可以查看我的博客《Hadoop序列化》 ,那么序列化后,其返回一個字節書組byteSeri,byteSeri里面有多少個字節?
分析:
在定義里指出,序列化格式為一個整數域和字節本身,
- 整數域是用來指定后面數據的字節數,我們知道byte[0],和byte[1]是兩個字節,所以,整數域的二進制為:00000000 00000000 00000000 00000010(4個字節),以16進制表示:00 00 00 02
- 字節本身就是byte[0]和byte[1]這兩個字節,所以,字節本身的二進制表示為:00000011 00000101,以16進制表示為:03 05
- 整個序列化數組的二進制表示為:00000000 00000000 00000000 00000010 00000011 00000101 ,以16進制表示為:00 00 00 02 03 05
那么上述的序列化后數組的長度為字節的個數,也就是 4 + 2 =6;拿例子來驗證:
Example:
1 package cn.roboson.writable; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.DataOutputStream; 5 import java.io.IOException; 6 7 import org.apache.hadoop.io.BytesWritable; 8 import org.apache.hadoop.io.Writable; 9 import org.apache.hadoop.util.StringUtils; 10 11 /** 12 * 1.定義一個二進制字節數組 13 * 2.將其序列化 14 * 3.由其序列化格式分析其內容 15 * @author roboson 16 * 17 */ 18 19 public class WritableText05 { 20 21 public static void main(String[] args) throws IOException { 22 //定義一個二進制字節數組 23 BytesWritable b = new BytesWritable(new byte[]{3,5}); 24 25 //輸出其長度,很明顯,只有兩個字節,其長度肯定是2 26 System.out.println("二進制數組的長度:"+b.getLength()); 27 28 //將其序列化,序列化可以查看我的博客《Hadoop序列化》 29 byte[] bytes=serialize(b); 30 //在上面的分析中,16進制的輸出為:00 00 00 02 03 05 31 System.out.println("序列化后以16進制表示:"+StringUtils.byteToHexString(bytes)); 32 33 //在上面的分析中,序列化后的數組長度為:6 34 System.out.println("序列化后的長度:"+bytes.length); 35 } 36 37 public static byte[] serialize(Writable writable) throws IOException{ 38 ByteArrayOutputStream out = new ByteArrayOutputStream(); 39 DataOutputStream dataOut = new DataOutputStream(out); 40 writable.write(dataOut); 41 return out.toByteArray(); 42 43 } 44 }
運行結果:
<2>可變性
和Text相似,BytesWritable是可變的,可以通過set()方法,設置進行修改。BytesWritable的getBytes()方法,返回的是字節數組的容量,而其存儲數據的實際大小,需要通過getLong()方法來查看
Example:
1 package cn.roboson.writable; 2 3 import org.apache.hadoop.io.BytesWritable; 4 5 public class WritableText06 { 6 7 public static void main(String[] args) { 8 BytesWritable b = new BytesWritable(new byte[]{3,5}); 9 System.out.println("字節數組的實際數據長度:"+b.getLength()); 10 System.out.println("字節數組的容量大小:"+b.getBytes().length); 11 12 //改變其容量 13 b.setCapacity(11); 14 //getLength()方法,返回的是實際數據的大小 15 System.out.println("改變容量后實際數據的大小:"+b.getLength()); 16 //getBytes().length返回的是容量大小 17 System.out.println("改變容量后容量的大小:"+b.getBytes().length); 18 } 19 }
運行結果:
2.NullWritable
NullWritable是Writable的一個特殊類型,它的序列化長度為0.它並不從數據流中讀取數據,也不寫入數據。它充當占位符;在MapReduce中,如果不需要使用健或者值,就可以將健或者值聲明為NullWritable——結果是存儲常量空值。
NullWritable是一個單例實例類型,可以通過靜態方法get()獲得其實例,public static NullWritable get() ;
Example:
1 package cn.roboson.writable; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.DataOutputStream; 5 import java.io.IOException; 6 7 import org.apache.hadoop.io.NullWritable; 8 import org.apache.hadoop.io.Writable; 9 /** 10 * 1.獲得一個NullWritable 11 * 2.序列化后,查看其長度 12 * @author roboson 13 * 14 */ 15 16 public class Writable02 { 17 18 public static void main(String[] args) throws IOException { 19 20 NullWritable writable = NullWritable.get(); 21 byte[] bytes = serialize(writable); 22 System.out.println("NullWritable序列化后的長度:"+bytes.length); 23 } 24 25 public static byte[] serialize(Writable writable) throws IOException{ 26 ByteArrayOutputStream out = new ByteArrayOutputStream(); 27 DataOutputStream dataOut = new DataOutputStream(out); 28 writable.write(dataOut); 29 return out.toByteArray(); 30 31 } 32 }
運行結果:
3.ObjectWritable
ObjectWritable是對Java基本類型(String、enum,Writable,null或這些類型組成的數組)的通用封裝。在Hadoop RPC中用於對方法的參數和返回類型進行封裝和解封裝。當一個字段中包含多個類型時,ObjectWritable是非常有用的,可以直接將類型聲明為ObjectWritable,但是,缺陷是非常浪費空間。舉個例子來看看,就知道有多么浪費!
Example:
1 package cn.roboson.writable; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.DataOutputStream; 5 import java.io.IOException; 6 7 import org.apache.hadoop.io.BytesWritable; 8 import org.apache.hadoop.io.ObjectWritable; 9 import org.apache.hadoop.io.Writable; 10 import org.apache.hadoop.util.StringUtils; 11 12 /** 13 * 1.新建一個ObjectWritable 14 * 2.將其序列化並查看其大小 15 * @author roboson 16 * 17 */ 18 public class Writable03 { 19 20 public static void main(String[] args) throws IOException { 21 22 BytesWritable bytes = new BytesWritable(new byte[]{3,5}); 23 byte[] byte1 = serialize(bytes); 24 //前面的介紹,可以知道,長度為6 25 System.out.println("bytes數組序列化后的長度:"+byte1.length); 26 System.out.println("bytes數組序列化的16進制表示:"+StringUtils.byteToHexString(byte1)); 27 28 ObjectWritable object = new ObjectWritable(); 29 object.set(new byte[]{3,5}); 30 byte[] byte2 = serialize(object); 31 System.out.println("ObjectWritable序列化后的長度:"+byte2.length); 32 System.out.println("ObjectWritable列化的16進制表示:"+StringUtils.byteToHexString(byte2)); 33 } 34 35 public static byte[] serialize(Writable writable) throws IOException{ 36 ByteArrayOutputStream out = new ByteArrayOutputStream(); 37 DataOutputStream dataOut = new DataOutputStream(out); 38 writable.write(dataOut); 39 return out.toByteArray(); 40 41 } 42 }
運行結果:
4.GenericWritable
通過上面的運行結果,已經知道ObjectWritable類是非常浪費空間的,如果封裝的類型數量比較少,這種情況下,可以用GenericWritable類來代替。它的效率更高一些。因為對序列化后的類型的引用加入位置索引。查看HadoopAPI幫助文檔,發現GenericWritable是一個抽象類:
那么如何使用它:
- 寫一個類,繼承自GenericWritable
- 重寫getTypes()方法
- 指定靜態類型數組中的值
Example:
MyGenericWritable.java
package cn.roboson.writable; import org.apache.hadoop.io.BytesWritable; import org.apache.hadoop.io.GenericWritable; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.io.Writable; public class MyGenericWritable extends GenericWritable{ private static Class[] CLASSES={ Text.class, BytesWritable.class, IntWritable.class }; @Override protected Class<? extends Writable>[] getTypes() { // TODO Auto-generated method stub return CLASSES; } }
Writable04.java
1 package cn.roboson.writable; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.DataOutputStream; 5 import java.io.IOException; 6 7 import org.apache.hadoop.io.BytesWritable; 8 import org.apache.hadoop.io.IntWritable; 9 import org.apache.hadoop.io.Text; 10 import org.apache.hadoop.io.Writable; 11 import org.apache.hadoop.util.StringUtils; 12 13 public class Writable04 { 14 15 public static void main(String[] args) throws IOException { 16 17 Text t = new Text("Hadoop"); 18 byte[] byte0 = serialize(t); 19 System.out.println("Text序列化后的長度:"+byte0.length); 20 System.out.println("Text序列化的16進制表示:"+StringUtils.byteToHexString(byte0)); 21 22 MyGenericWritable genericText = new MyGenericWritable(); 23 genericText.set(t); 24 byte[] byteText = serialize(genericText); 25 System.out.println("TextGenericWritable序列化后的長度:"+byteText.length); 26 System.out.println("TextGenericWritable列化的16進制表示:"+StringUtils.byteToHexString(byteText)); 27 28 BytesWritable bytes = new BytesWritable(new byte[]{3,5}); 29 byte[] byte1 = serialize(bytes); 30 //前面的介紹,可以知道,長度為6 31 System.out.println("bytes數組序列化后的長度:"+byte1.length); 32 System.out.println("bytes數組序列化的16進制表示:"+StringUtils.byteToHexString(byte1)); 33 34 MyGenericWritable generic = new MyGenericWritable(); 35 generic.set(bytes); 36 byte[] byteBytes = serialize(generic); 37 System.out.println("GenericWritable序列化后的長度:"+byteBytes.length); 38 System.out.println("GenericWritable列化的16進制表示:"+StringUtils.byteToHexString(byteBytes)); 39 40 41 IntWritable intWritable = new IntWritable(2); 42 byte[] byte2 = serialize(intWritable); 43 System.out.println("IntWritable序列化后的長度:"+byte2.length); 44 System.out.println("IntWritable序列化的16進制表示:"+StringUtils.byteToHexString(byte2)); 45 46 MyGenericWritable genericInt = new MyGenericWritable(); 47 genericInt.set(intWritable); 48 byte[] byteInt = serialize(genericInt); 49 System.out.println("IntGenericWritable序列化后的長度:"+byteInt.length); 50 System.out.println("IntGenericWritable列化的16進制表示:"+StringUtils.byteToHexString(byteInt)); 51 52 } 53 public static byte[] serialize(Writable writable) throws IOException{ 54 ByteArrayOutputStream out = new ByteArrayOutputStream(); 55 DataOutputStream dataOut = new DataOutputStream(out); 56 writable.write(dataOut); 57 return out.toByteArray(); 58 59 } 60 }
運行結果:
可以發現,GenericWritable比ObjectWritable更節省空間,和本來的相比,只增加了一個字節,並且這個字節是其在靜態數組CLASSES的下標號!