天天看點

自定義Hadoop的可序列化類

java原生語言中要想一個自定義類可序列化,很簡單,隻要讓這個類實作java.io.Serializable接口就可以了,但是在Hadoop架構中,要想讓自定義類可以被序列化,我們必須手動讓其實作WritableCompable接口并且實作write(),readFields(),compareTo()方法。

下面就是一個我們自定義的可序列化的類:

  1. /*  
  2.  */ 
  3. package com.charles.writable; 
  4. import java.io.DataInput; 
  5. import java.io.DataOutput; 
  6. import java.io.IOException; 
  7. import org.apache.hadoop.io.IntWritable; 
  8. import org.apache.hadoop.io.Text; 
  9. import org.apache.hadoop.io.WritableComparable; 
  10. /** 
  11.  * 
  12.  * Description: 這是自定義的Hadoop序列化類,它可以用Hadoop序列化反序列化這個類 
  13.  * 
  14.  * @author charles.wang 
  15.  * @created Jun 2, 2012 11:19:25 AM 
  16.  *  
  17.  */ 
  18. public class PersonWritable implements WritableComparable<PersonWritable> { 
  19.     private Text name; 
  20.     private IntWritable age; 
  21.     private Text title; 
  22.     public PersonWritable(){ 
  23.        set("someperson",0,"sometitle"); 
  24.     } 
  25.     public PersonWritable(String name ,int age, String title){ 
  26.         set(name,age,title); 
  27.     } 
  28.     public void set(String name ,int age,String title){ 
  29.         this.name =  new Text(name); 
  30.         age=(age>0)?age:1; 
  31.         this.age = new IntWritable(age); 
  32.         this.title=  new Text(title); 
  33.     } 
  34.     /** 
  35.      *  這個方法用于定義序列化過程,它把這個對象的所有字段依次序列化 
  36.      */ 
  37.     @Override 
  38.     public void write(DataOutput out) throws IOException { 
  39.         // TODO Auto-generated method stub 
  40.         name.write(out); 
  41.         age.write(out); 
  42.         title.write(out); 
  43.     } 
  44.     /** 
  45.      *  這個方法用于定義反序列化過程,它吧序列化後的DataInput的内容還原為Hadoop對象 
  46.      */ 
  47.     @Override 
  48.     public void readFields(DataInput in) throws IOException { 
  49.         // TODO Auto-generated method stub 
  50.         name.readFields(in); 
  51.         age.readFields(in); 
  52.         title.readFields(in); 
  53.     } 
  54.     /** 
  55.      * 這是用于2個序列化對象之間的比較 
  56.      */ 
  57.     @Override 
  58.     public int compareTo(PersonWritable pO) { 
  59.         // TODO Auto-generated method stub 
  60.         int cmp1 = name.compareTo(pO.name); 
  61.         if(cmp1 != 0){ 
  62.             return cmp1; 
  63.         } 
  64.         int cmp2 = age.compareTo(pO.age); 
  65.         if(cmp2 !=0){ 
  66.             return cmp2; 
  67.         } 
  68.         int cmp3 = title.compareTo(pO.title); 
  69.         return cmp3; 
  70.     } 
  71.     /** 
  72.      * 定義hashcode是個好習慣,我們還是使用最常用的字段分别乘以不同的素數然後相加的方法 
  73.      */ 
  74.     @Override 
  75.     public int hashCode(){ 
  76.         return name.hashCode()*71+ age.hashCode()*73+title.hashCode()*127; 
  77.     } 
  78.     @Override 
  79.     public boolean equals (Object o ){ 
  80.         if ( o instanceof PersonWritable){ 
  81.             PersonWritable pw = (PersonWritable) o; 
  82.             boolean equals = name.equals(pw.name) && age.equals(pw.age) && title.equals(pw.title); 
  83.             return equals; 
  84.         } 
  85.         return false; 
  86.     } 
  87.     @Override 
  88.     public String toString(){ 
  89.         StringBuffer sb = new StringBuffer(); 
  90.         sb.append("["); 
  91.         sb.append("姓名: "+name+","); 
  92.         sb.append("年齡: "+age+","); 
  93.         sb.append("頭銜: "+title); 
  94.         sb.append("]"); 
  95.         return sb.toString(); 
  96.     } 

為了友善示範序列化前後的内容,我們定義了一個工具方法,這個方法可以用于跟蹤序列化和反序列化的中間産物:

  1. /*  
  2.  */ 
  3. package com.charles.writable; 
  4. import java.io.ByteArrayInputStream; 
  5. import java.io.ByteArrayOutputStream; 
  6. import java.io.DataInputStream; 
  7. import java.io.DataOutputStream; 
  8. import java.io.IOException; 
  9. import org.apache.hadoop.io.Writable; 
  10. /** 
  11.  * 
  12.  * Description: 這個類提供了工具方法來記錄序列化的軌迹 
  13.  * 因為,在hadoop中序列化和反序列化都是在Writable接口中進行的,Writable是被序列化的Hadoop對象 
  14.  * 是以我們把序列化的産物存到位元組數組中進而可以捕捉到内容 
  15.  * 
  16.  * @author charles.wang 
  17.  * @created Jun 2, 2012 9:32:41 AM 
  18.  *  
  19.  */ 
  20. public class HadoopSerializationUtil { 
  21.     //這個方法可以把Hadoop的對象(Writable表示這個是可以序列化的)序列化到位元組數組中, 
  22.     //然後把位元組數組中的内容傳回出來 
  23.     //入參,被序列化的數值對象 
  24.     //傳回值:序列化後的位元組數組 
  25.     public static byte[] serialize(Writable writable) throws IOException { 
  26.         //建立一個位元組數組 
  27.          ByteArrayOutputStream out = new ByteArrayOutputStream(); 
  28.          //建立一個DataOutputStream,并且包裝位元組數組,用于存放序列化後的位元組流 
  29.          DataOutputStream dataout =  new DataOutputStream(out); 
  30.          //讓參數的Hadoop對象序列化到位元組流中 
  31.          writable.write(dataout); 
  32.          dataout.close(); 
  33.          //傳回序列化後的位元組流 
  34.          return out.toByteArray(); 
  35.     } 
  36.     //這個方法用于反序列化一個位元組數組成Hadoop Writable對象 
  37.     //入參1:反序列化後的Writable對象存放在這個參數中 
  38.     //入參2:被反序列化的位元組數組 
  39.     public static void deserialize(Writable writable,byte[] bytes) throws Exception{ 
  40.         //打開一個位元組數組輸入流讓其指向即将要被處理的位元組數組(第二個參數) 
  41.         ByteArrayInputStream in = new ByteArrayInputStream(bytes); 
  42.         //打開一個DataInputStream 
  43.         DataInputStream datain = new DataInputStream(in); 
  44.         //讓Hadoop架構反序列化這個位元組數組,還原後的Writable對象存放到第一個參數中 
  45.         writable.readFields(datain); 
  46.         datain.close(); 
  47.     } 
  1. /*  
  2.  */ 
  3. package com.charles.writable; 
  4. import org.apache.hadoop.util.StringUtils; 
  5. /** 
  6.  * 
  7.  * Description: 這個例子用于展示自定義的Hadoop序列化類是否工作正常 
  8.  * 
  9.  * @author charles.wang 
  10.  * @created Jun 2, 2012 11:40:01 AM 
  11.  *  
  12.  */ 
  13. public class HadoopObjectSerializationDemo { 
  14.     public static void main(String [] args) throws Exception{ 
  15.         //第一個實驗,把我們自定義的Hadoop可序列化對象進行序列化 
  16.         System.out.println("實驗1: 序列化"); 
  17.         PersonWritable originalPersonWritable = new PersonWritable("Charles Wang" ,26 ,"Technical Lead"); 
  18.         String typeInfo= "被測試的自定義Hadoop可序列化類類型為: "+originalPersonWritable.getClass().getName()+"\n"; 
  19.         String primaryPersonWritableInfo = "序列化前對象為:  "+originalPersonWritable.toString()+"\n"; 
  20.         //開始序列化過程 
  21.         byte[] serializedHadoopValue =HadoopSerializationUtil.serialize(originalPersonWritable); 
  22.         String lengthInfo= "序列化後的位元組數組長度為: "+serializedHadoopValue.length+"\n"; 
  23.         String serializeValueInfo= "序列化後的值為: " +StringUtils.byteToHexString(serializedHadoopValue)+"\n"; 
  24.         System.out.println(typeInfo+primaryPersonWritableInfo+lengthInfo+serializeValueInfo+"\n"); 
  25.         System.out.println(); 
  26.         //第二個實驗,把我們序列化之後的位元組數組反序列化為原始Hadoop對象 
  27.         System.out.println("實驗2:反序列化"); 
  28.         PersonWritable restoredPersonWritable = new PersonWritable(); 
  29.         String originalByteArrayInfo="被反序列化的位元組數組内容為: "+StringUtils.byteToHexString(serializedHadoopValue)+"\n";
  30.         //開始反序列化過程 
  31.         HadoopSerializationUtil.deserialize(restoredPersonWritable, serializedHadoopValue); 
  32.         String restoredValueInfo = "反序列化之後的Writable對象為: "+restoredPersonWritable.toString(); 
  33.         System.out.println(originalByteArrayInfo+restoredValueInfo+"\n"); 
  34.     } 
  1. 實驗1: 序列化 
  2. 被測試的自定義Hadoop可序列化類類型為: com.charles.writable.PersonWritable 
  3. 序列化前對象為:  [姓名: Charles Wang,年齡: 26,頭銜: Technical Lead] 
  4. 序列化後的位元組數組長度為: 32 
  5. 序列化後的值為: 0c436861726c65732057616e670000001a0e546563686e6963616c204c656164 
  6. 實驗2:反序列化 
  7. 被反序列化的位元組數組内容為: 0c436861726c65732057616e670000001a0e546563686e6963616c204c656164 
  8. 反序列化之後的Writable對象為: [姓名: Charles Wang,年齡: 26,頭銜: Technical Lead] 

繼續閱讀