Hadoop基础-序列化与反序列化(实现Writable接口)
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的大小的话,估计差距就是天壤之别啦!
本文来自博客园,作者:尹正杰,转载请注明原文链接:https://www.cnblogs.com/yinzhengjie/p/9108465.html,个人微信: "JasonYin2020"(添加时请备注来源及意图备注,有偿付费)
当你的才华还撑不起你的野心的时候,你就应该静下心来学习。当你的能力还驾驭不了你的目标的时候,你就应该沉下心来历练。问问自己,想要怎样的人生。