java原生語言中要想一個自定義類可序列化,很簡單,隻要讓這個類實作java.io.Serializable接口就可以了,但是在Hadoop架構中,要想讓自定義類可以被序列化,我們必須手動讓其實作WritableCompable接口并且實作write(),readFields(),compareTo()方法。
下面就是一個我們自定義的可序列化的類:
- /*
- */
- package com.charles.writable;
- import java.io.DataInput;
- import java.io.DataOutput;
- import java.io.IOException;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.io.WritableComparable;
- /**
- *
- * Description: 這是自定義的Hadoop序列化類,它可以用Hadoop序列化反序列化這個類
- *
- * @author charles.wang
- * @created Jun 2, 2012 11:19:25 AM
- *
- */
- public class PersonWritable implements WritableComparable<PersonWritable> {
- private Text name;
- private IntWritable age;
- private Text title;
- public PersonWritable(){
- set("someperson",0,"sometitle");
- }
- public PersonWritable(String name ,int age, String title){
- set(name,age,title);
- }
- public void set(String name ,int age,String title){
- this.name = new Text(name);
- age=(age>0)?age:1;
- this.age = new IntWritable(age);
- this.title= new Text(title);
- }
- /**
- * 這個方法用于定義序列化過程,它把這個對象的所有字段依次序列化
- */
- @Override
- public void write(DataOutput out) throws IOException {
- // TODO Auto-generated method stub
- name.write(out);
- age.write(out);
- title.write(out);
- }
- /**
- * 這個方法用于定義反序列化過程,它吧序列化後的DataInput的内容還原為Hadoop對象
- */
- @Override
- public void readFields(DataInput in) throws IOException {
- // TODO Auto-generated method stub
- name.readFields(in);
- age.readFields(in);
- title.readFields(in);
- }
- /**
- * 這是用于2個序列化對象之間的比較
- */
- @Override
- public int compareTo(PersonWritable pO) {
- // TODO Auto-generated method stub
- int cmp1 = name.compareTo(pO.name);
- if(cmp1 != 0){
- return cmp1;
- }
- int cmp2 = age.compareTo(pO.age);
- if(cmp2 !=0){
- return cmp2;
- }
- int cmp3 = title.compareTo(pO.title);
- return cmp3;
- }
- /**
- * 定義hashcode是個好習慣,我們還是使用最常用的字段分别乘以不同的素數然後相加的方法
- */
- @Override
- public int hashCode(){
- return name.hashCode()*71+ age.hashCode()*73+title.hashCode()*127;
- }
- @Override
- public boolean equals (Object o ){
- if ( o instanceof PersonWritable){
- PersonWritable pw = (PersonWritable) o;
- boolean equals = name.equals(pw.name) && age.equals(pw.age) && title.equals(pw.title);
- return equals;
- }
- return false;
- }
- @Override
- public String toString(){
- StringBuffer sb = new StringBuffer();
- sb.append("[");
- sb.append("姓名: "+name+",");
- sb.append("年齡: "+age+",");
- sb.append("頭銜: "+title);
- sb.append("]");
- return sb.toString();
- }
- }
為了友善示範序列化前後的内容,我們定義了一個工具方法,這個方法可以用于跟蹤序列化和反序列化的中間産物:
- /*
- */
- package com.charles.writable;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import org.apache.hadoop.io.Writable;
- /**
- *
- * Description: 這個類提供了工具方法來記錄序列化的軌迹
- * 因為,在hadoop中序列化和反序列化都是在Writable接口中進行的,Writable是被序列化的Hadoop對象
- * 是以我們把序列化的産物存到位元組數組中進而可以捕捉到内容
- *
- * @author charles.wang
- * @created Jun 2, 2012 9:32:41 AM
- *
- */
- public class HadoopSerializationUtil {
- //這個方法可以把Hadoop的對象(Writable表示這個是可以序列化的)序列化到位元組數組中,
- //然後把位元組數組中的内容傳回出來
- //入參,被序列化的數值對象
- //傳回值:序列化後的位元組數組
- public static byte[] serialize(Writable writable) throws IOException {
- //建立一個位元組數組
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- //建立一個DataOutputStream,并且包裝位元組數組,用于存放序列化後的位元組流
- DataOutputStream dataout = new DataOutputStream(out);
- //讓參數的Hadoop對象序列化到位元組流中
- writable.write(dataout);
- dataout.close();
- //傳回序列化後的位元組流
- return out.toByteArray();
- }
- //這個方法用于反序列化一個位元組數組成Hadoop Writable對象
- //入參1:反序列化後的Writable對象存放在這個參數中
- //入參2:被反序列化的位元組數組
- public static void deserialize(Writable writable,byte[] bytes) throws Exception{
- //打開一個位元組數組輸入流讓其指向即将要被處理的位元組數組(第二個參數)
- ByteArrayInputStream in = new ByteArrayInputStream(bytes);
- //打開一個DataInputStream
- DataInputStream datain = new DataInputStream(in);
- //讓Hadoop架構反序列化這個位元組數組,還原後的Writable對象存放到第一個參數中
- writable.readFields(datain);
- datain.close();
- }
- }
- /*
- */
- package com.charles.writable;
- import org.apache.hadoop.util.StringUtils;
- /**
- *
- * Description: 這個例子用于展示自定義的Hadoop序列化類是否工作正常
- *
- * @author charles.wang
- * @created Jun 2, 2012 11:40:01 AM
- *
- */
- public class HadoopObjectSerializationDemo {
- public static void main(String [] args) throws Exception{
- //第一個實驗,把我們自定義的Hadoop可序列化對象進行序列化
- System.out.println("實驗1: 序列化");
- PersonWritable originalPersonWritable = new PersonWritable("Charles Wang" ,26 ,"Technical Lead");
- String typeInfo= "被測試的自定義Hadoop可序列化類類型為: "+originalPersonWritable.getClass().getName()+"\n";
- String primaryPersonWritableInfo = "序列化前對象為: "+originalPersonWritable.toString()+"\n";
- //開始序列化過程
- byte[] serializedHadoopValue =HadoopSerializationUtil.serialize(originalPersonWritable);
- String lengthInfo= "序列化後的位元組數組長度為: "+serializedHadoopValue.length+"\n";
- String serializeValueInfo= "序列化後的值為: " +StringUtils.byteToHexString(serializedHadoopValue)+"\n";
- System.out.println(typeInfo+primaryPersonWritableInfo+lengthInfo+serializeValueInfo+"\n");
- System.out.println();
- //第二個實驗,把我們序列化之後的位元組數組反序列化為原始Hadoop對象
- System.out.println("實驗2:反序列化");
- PersonWritable restoredPersonWritable = new PersonWritable();
- String originalByteArrayInfo="被反序列化的位元組數組内容為: "+StringUtils.byteToHexString(serializedHadoopValue)+"\n";
- //開始反序列化過程
- HadoopSerializationUtil.deserialize(restoredPersonWritable, serializedHadoopValue);
- String restoredValueInfo = "反序列化之後的Writable對象為: "+restoredPersonWritable.toString();
- System.out.println(originalByteArrayInfo+restoredValueInfo+"\n");
- }
- }
- 實驗1: 序列化
- 被測試的自定義Hadoop可序列化類類型為: com.charles.writable.PersonWritable
- 序列化前對象為: [姓名: Charles Wang,年齡: 26,頭銜: Technical Lead]
- 序列化後的位元組數組長度為: 32
- 序列化後的值為: 0c436861726c65732057616e670000001a0e546563686e6963616c204c656164
- 實驗2:反序列化
- 被反序列化的位元組數組内容為: 0c436861726c65732057616e670000001a0e546563686e6963616c204c656164
- 反序列化之後的Writable對象為: [姓名: Charles Wang,年齡: 26,頭銜: Technical Lead]