Hadoop基礎-序列化與反序列化(實現Writable接口)
作者:尹正傑
版權聲明:原創作品,謝絕轉載!否則將追究法律責任。
一.序列化簡介
1>.什么是序列化
序列化也稱串行化,是將結構化的對象轉換成字節流,以便在網絡上進行傳輸或者寫入到磁盤進行永久性存儲的過程。
2>.什么是反序列化
反序列化也稱反串行化,它是指將字節流轉回結構化對象的逆過程。
3>.序列化的應用
主要用於分布式數據處理的兩大領域,即進程間通信和永久存儲。
4>.序列化的特點
第一:緊湊,體積小,節省帶寬;
第二:快速,序列化過程快速;
第三:可擴展性(向下兼容),新API支持舊數據格式;
第四:支持互操作,跨語言(可惜的是Java序列化和hadoop序列化都沒有實現該屬性!);
遺憾的是,Java和hadoop序列化都不支持上述的第四點特性,即跨語言。目前流行的兩個序列化框架avro和protobuf(由Google公司研發)都支持以上四個特性喲!這兩個框架不是本篇博客的重點,后期我會寫兩篇關於這兩個序列化框架筆記。
二.hadoop串行化介紹
1>.為什么Hadoop要自己定義Hadoop串行化
之前我分享過Java序列化的通過ObjectOutputStream流對象可以對任意實現Serializable類接口進行實例化操作的筆記。通過ObjectInputStream流對象可以進行反序列化操作,詳情請參考:https://www.cnblogs.com/yinzhengjie/p/8988003.html。
遺憾的是Hadoop並沒有使用ObjectOutputStream進行序列化操作,而是自己定義了序列化的格式。可能你會跟當初剛剛學習Hadoop的我問同樣的問題:“為什么Hadoop不Java自己提供的實現Serializable接口的方式進行序列化操作呢?”,每一件事物的存在都有他的原因,Hadoop自己定義了序列話接口是Hadoop處理的數據是海量的,他們對數據的存儲,以及壓縮算法是有要求的,感興趣的小伙伴可以對一個較大數據進行序列化操作,你會發現Hadoop的序列化方式的確挺牛的!
2>.hadoop串行化格式
Hadoop把Java的幾種數據類型進行了封裝,將Long類型的數據封裝為LongWritable,將int類型的數據進行封裝為IntWritable類型,將String類型數據封裝為Text類型,將Byte類型封裝為ByteWriterable,將Array類型封裝為ArrayWritale類型等等;
三.比較Java和Hadoop對int類型的串行化格式
1>.Java對int值2018進行序列化和反序列化的代碼如下
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 package cn.org.yinzhengjie.serialize; 7 8 import java.io.*; 9 10 public class JavaSerial { 11 public final static String fileParh = "D:\\10.Java\\IDE\\yhinzhengjieData\\java.serial"; 12 public static void main(String[] args) throws Exception { 13 intSerialze(); 14 intDeserialize(); 15 } 16 //定義序列化方式 17 public static void intSerialze() throws IOException { 18 Integer i = 2018; 19 FileOutputStream fos = new FileOutputStream(fileParh); 20 ObjectOutputStream oos = new ObjectOutputStream(fos); 21 //進行Java的序列化方式 22 oos.writeInt(i); 23 //釋放資源 24 oos.close(); 25 fos.close(); //這里其實可以不用寫,因為上面一行釋放資源會順帶把它封裝的對線下也關流了,不過這行即使咱們寫了也是不會報錯的! 26 } 27 //定義反序列化方法 28 public static void intDeserialize() throws Exception { 29 FileInputStream fis = new FileInputStream(fileParh); 30 ObjectInputStream ois = new ObjectInputStream(fis); 31 //調用反序列化流的方法"readInt()"讀取對象,要注意的是反序列話的對象需要存在相應的字節碼文件。否則會拋異常 32 int res = ois.readInt(); 33 //釋放資源 34 ois.close(); 35 fis.close(); 36 System.out.println(res); 37 } 38 } 39 40 41 /* 42 以上代碼執行結果如下: 43 2018 44 */
2>.Hadoop對int類型的序列化方式和反序列化的代碼如下
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 package cn.org.yinzhengjie.serialize; 7 8 import org.apache.hadoop.io.IntWritable; 9 import java.io.*; 10 11 public class HadoopSerial { 12 public final static String fileParh = "D:\\10.Java\\IDE\\yhinzhengjieData\\Datahadoop.serial"; 13 public static void main(String[] args) throws IOException { 14 intSerialze(); 15 intDeserialize(); 16 } 17 18 //定義序列化方式 19 public static void intSerialze() throws IOException { 20 //初始化intWritable 21 IntWritable iw = new IntWritable(2018); 22 FileOutputStream fos = new FileOutputStream(fileParh); 23 DataOutputStream dos = new DataOutputStream(fos); 24 //進行Hadoop的序列化方式 25 iw.write(dos); 26 //別忘記釋放資源喲 27 dos.close(); 28 fos.close(); 29 } 30 31 //定義反序列化方式 32 public static void intDeserialize() throws IOException { 33 //初始化intWritable 34 IntWritable iw = new IntWritable(); 35 FileInputStream fis = new FileInputStream(fileParh); 36 DataInputStream dis = new DataInputStream(fis); 37 //進行Hadoop的反序列化方式,將數據輸入流的數據傳遞給iw對象的readFields方法。 38 iw.readFields(dis); 39 //再通過iw對象的get方法獲取數據 40 int res = iw.get(); 41 System.out.println(res); 42 } 43 } 44 45 /* 46 以上代碼執行結果如下: 47 2018 48 */
3>.查看兩種方式的序列化文件大小
Datahadoop.serial 文件屬性如下:
java.serial 文件屬性如下:
同樣都是對一個int類型值為2018的數字進行序列化,為什么Hadoop序列化只需要4個字節,而Java卻需要10個字節呢?如果數字是PB的數據量,在選擇序列化的方案上你會選擇哪個呢?
四.比較java與Hadoop對自定義類的串行化格式

1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.serialize; 8 9 import java.io.Serializable; 10 11 public class Student implements Serializable { 12 private String name; 13 private int age; 14 private boolean ismarry; 15 16 public String getName() { 17 return name; 18 } 19 20 public int getAge() { 21 return age; 22 } 23 24 public boolean isIsmarry() { 25 return ismarry; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 32 public void setAge(int age) { 33 this.age = age; 34 } 35 36 public void setIsmarry(boolean ismarry) { 37 this.ismarry = ismarry; 38 } 39 40 @Override 41 public String toString() { 42 return "Student{" + 43 "name='" + name + '\'' + 44 ", age=" + age + 45 ", ismarry=" + ismarry + 46 '}'; 47 } 48 }
1>.java對自定義Student類實現序列化和反序列化
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 package cn.org.yinzhengjie.serialize; 7 8 import java.io.*; 9 10 public class JavaSerial { 11 public final static String fileParh = "D:\\10.Java\\IDE\\yhinzhengjieData\\java.student"; 12 public static void main(String[] args) throws Exception { 13 studentSerialze(); 14 studentDeserialize(); 15 } 16 //定義序列化方式 17 public static void studentSerialze() throws IOException { 18 //實例化對象yzj 19 Student yzj = new Student(); 20 yzj.setName("尹正傑"); 21 yzj.setAge(18); 22 yzj.setIsmarry(false); 23 FileOutputStream fos = new FileOutputStream(fileParh); 24 ObjectOutputStream oos = new ObjectOutputStream(fos); 25 //進行Java的序列化方式 26 oos.writeObject(yzj); 27 //釋放資源 28 oos.close(); 29 fos.close(); //這里其實可以不用寫,因為上面一行釋放資源會順帶把它封裝的對線下也關流了,不過這行即使咱們寫了也是不會報錯的! 30 } 31 //定義反序列化方法 32 public static void studentDeserialize() throws Exception { 33 FileInputStream fis = new FileInputStream(fileParh); 34 ObjectInputStream ois = new ObjectInputStream(fis); 35 //調用反序列化流的方法"readObject()"讀取對象,要注意的是反序列話的對象需要存在相應的字節碼文件。否則會拋異常 36 Object res = ois.readObject(); 37 //釋放資源 38 ois.close(); 39 fis.close(); 40 System.out.println(res); 41 } 42 } 43 44 45 /* 46 以上代碼執行結果如下: 47 Student{name='尹正傑', age=18, ismarry=false} 48 */
2>.Hadoop對自定義Student類實現序列化和反序列化
Hadoop對自定義類實現序列化或者反序列化操作的話,需要實現Hadoop的Writable接口,接下來我們舉個例子,代碼如下:
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 7 package cn.org.yinzhengjie.serialize; 8 9 import org.apache.hadoop.io.Writable; 10 11 import java.io.DataInput; 12 import java.io.DataOutput; 13 import java.io.IOException; 14 15 public class StudentWirtable implements Writable { 16 17 //切記這里需要給student賦值,不然可能會報錯空指針異常喲! 18 private Student student = new Student(); 19 20 public Student getStudent() { 21 return student; 22 } 23 24 public void setStudent(Student student) { 25 this.student = student; 26 } 27 28 //定義串行化的方法 29 public void write(DataOutput dataOutput) throws IOException { 30 //定義自定義類的序列化順序,我這里是先序列化name,在序列化age,最好才序列化ismarry。 31 dataOutput.writeUTF(student.getName()); 32 dataOutput.writeInt(student.getAge()); 33 dataOutput.writeBoolean(student.isIsmarry()); 34 35 } 36 37 //定義反串行化的方法 38 public void readFields(DataInput dataInput) throws IOException { 39 student.setName(dataInput.readUTF()); 40 student.setAge(dataInput.readInt()); 41 student.setIsmarry(dataInput.readBoolean()); 42 } 43 }
接下來就是我們調用自己定義的序列化方法啦,測試代碼如下:
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 package cn.org.yinzhengjie.serialize; 7 8 import java.io.*; 9 10 public class HadoopSerial { 11 public final static String fileParh = "D:\\10.Java\\IDE\\yhinzhengjieData\\hadoop.student"; 12 public static void main(String[] args) throws IOException { 13 studentSerialze(); 14 studentDeserialize(); 15 } 16 17 //定義序列化方式 18 public static void studentSerialze() throws IOException { 19 //實例化對象yzj 20 Student yzj = new Student(); 21 yzj.setName("尹正傑"); 22 yzj.setAge(18); 23 yzj.setIsmarry(false); 24 //初始化StudentWirtable,這是咱們定義的一個容器 25 StudentWirtable sw = new StudentWirtable(); 26 sw.setStudent(yzj); 27 FileOutputStream fos = new FileOutputStream(fileParh); 28 DataOutputStream dos = new DataOutputStream(fos); 29 //進行Hadoop的序列化方式 30 sw.write(dos); 31 //別忘記釋放資源喲 32 dos.close(); 33 fos.close(); 34 } 35 36 //定義反序列化方式 37 public static void studentDeserialize() throws IOException { 38 //初始化intWritable 39 StudentWirtable sw = new StudentWirtable(); 40 DataInputStream dis = new DataInputStream(new FileInputStream(fileParh)); 41 sw.readFields(dis); 42 Student yzj = sw.getStudent(); 43 dis.close(); 44 System.out.println(yzj.toString()); 45 46 } 47 } 48 49 /* 50 以上代碼執行結果如下: 51 Student{name='尹正傑', age=18, ismarry=false} 52 */
3>.查看兩種方式的序列化文件大小
hadoop.student 文件屬性如下:
java.student 文件屬性如下:
如果一個int類型你感覺不出來hadoop序列化和java序列化的區別,那么自定義類的屬性進行序列化你應該明顯的看出來hadoop序列化要比java傳統的序列化方式要節省空間多的多,如果這個數據換成一個PB的大小的話,估計差距就是天壤之別啦!