首页 > 技术文章 > Hadoop基础-序列化与反序列化(实现Writable接口)

yinzhengjie 2018-05-29 23:02 原文

                   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 }
Student.java 文件内容

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的大小的话,估计差距就是天壤之别啦!

 

 

 

推荐阅读