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进行序列化和反序列化的代码如下

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.serialize; import java.io.*; public class JavaSerial {
public final static String fileParh = "D:\\10.Java\\IDE\\yhinzhengjieData\\java.serial";
public static void main(String[] args) throws Exception {
intSerialze();
intDeserialize();
}
//定义序列化方式
public static void intSerialze() throws IOException {
Integer i = 2018;
FileOutputStream fos = new FileOutputStream(fileParh);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//进行Java的序列化方式
oos.writeInt(i);
//释放资源
oos.close();
fos.close(); //这里其实可以不用写,因为上面一行释放资源会顺带把它封装的对线下也关流了,不过这行即使咱们写了也是不会报错的!
}
//定义反序列化方法
public static void intDeserialize() throws Exception {
FileInputStream fis = new FileInputStream(fileParh);
ObjectInputStream ois = new ObjectInputStream(fis);
//调用反序列化流的方法"readInt()"读取对象,要注意的是反序列话的对象需要存在相应的字节码文件。否则会抛异常
int res = ois.readInt();
//释放资源
ois.close();
fis.close();
System.out.println(res);
}
} /*
以上代码执行结果如下:
2018
*/

2>.Hadoop对int类型的序列化方式和反序列化的代码如下

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.serialize; import org.apache.hadoop.io.IntWritable;
import java.io.*; public class HadoopSerial {
public final static String fileParh = "D:\\10.Java\\IDE\\yhinzhengjieData\\Datahadoop.serial";
public static void main(String[] args) throws IOException {
intSerialze();
intDeserialize();
} //定义序列化方式
public static void intSerialze() throws IOException {
//初始化intWritable
IntWritable iw = new IntWritable(2018);
FileOutputStream fos = new FileOutputStream(fileParh);
DataOutputStream dos = new DataOutputStream(fos);
//进行Hadoop的序列化方式
iw.write(dos);
//别忘记释放资源哟
dos.close();
fos.close();
} //定义反序列化方式
public static void intDeserialize() throws IOException {
//初始化intWritable
IntWritable iw = new IntWritable();
FileInputStream fis = new FileInputStream(fileParh);
DataInputStream dis = new DataInputStream(fis);
//进行Hadoop的反序列化方式,将数据输入流的数据传递给iw对象的readFields方法。
iw.readFields(dis);
//再通过iw对象的get方法获取数据
int res = iw.get();
System.out.println(res);
}
} /*
以上代码执行结果如下:
2018
*/

3>.查看两种方式的序列化文件大小

  Datahadoop.serial 文件属性如下:

  java.serial 文件属性如下:

  同样都是对一个int类型值为2018的数字进行序列化,为什么Hadoop序列化只需要4个字节,而Java却需要10个字节呢?如果数字是PB的数据量,在选择序列化的方案上你会选择哪个呢?

四.比较java与Hadoop对自定义类的串行化格式

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package cn.org.yinzhengjie.serialize; import java.io.Serializable; public class Student implements Serializable {
private String name;
private int age;
private boolean ismarry; public String getName() {
return name;
} public int getAge() {
return age;
} public boolean isIsmarry() {
return ismarry;
} public void setName(String name) {
this.name = name;
} public void setAge(int age) {
this.age = age;
} public void setIsmarry(boolean ismarry) {
this.ismarry = ismarry;
} @Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", ismarry=" + ismarry +
'}';
}
}

Student.java 文件内容

1>.java对自定义Student类实现序列化和反序列化

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.serialize; import java.io.*; public class JavaSerial {
public final static String fileParh = "D:\\10.Java\\IDE\\yhinzhengjieData\\java.student";
public static void main(String[] args) throws Exception {
studentSerialze();
studentDeserialize();
}
//定义序列化方式
public static void studentSerialze() throws IOException {
//实例化对象yzj
Student yzj = new Student();
yzj.setName("尹正杰");
yzj.setAge(18);
yzj.setIsmarry(false);
FileOutputStream fos = new FileOutputStream(fileParh);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//进行Java的序列化方式
oos.writeObject(yzj);
//释放资源
oos.close();
fos.close(); //这里其实可以不用写,因为上面一行释放资源会顺带把它封装的对线下也关流了,不过这行即使咱们写了也是不会报错的!
}
//定义反序列化方法
public static void studentDeserialize() throws Exception {
FileInputStream fis = new FileInputStream(fileParh);
ObjectInputStream ois = new ObjectInputStream(fis);
//调用反序列化流的方法"readObject()"读取对象,要注意的是反序列话的对象需要存在相应的字节码文件。否则会抛异常
Object res = ois.readObject();
//释放资源
ois.close();
fis.close();
System.out.println(res);
}
} /*
以上代码执行结果如下:
Student{name='尹正杰', age=18, ismarry=false}
*/

2>.Hadoop对自定义Student类实现序列化和反序列化

  Hadoop对自定义类实现序列化或者反序列化操作的话,需要实现Hadoop的Writable接口,接下来我们举个例子,代码如下:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package cn.org.yinzhengjie.serialize; import org.apache.hadoop.io.Writable; import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException; public class StudentWirtable implements Writable { //切记这里需要给student赋值,不然可能会报错空指针异常哟!
private Student student = new Student(); public Student getStudent() {
return student;
} public void setStudent(Student student) {
this.student = student;
} //定义串行化的方法
public void write(DataOutput dataOutput) throws IOException {
//定义自定义类的序列化顺序,我这里是先序列化name,在序列化age,最好才序列化ismarry。
dataOutput.writeUTF(student.getName());
dataOutput.writeInt(student.getAge());
dataOutput.writeBoolean(student.isIsmarry()); } //定义反串行化的方法
public void readFields(DataInput dataInput) throws IOException {
student.setName(dataInput.readUTF());
student.setAge(dataInput.readInt());
student.setIsmarry(dataInput.readBoolean());
}
}

  接下来就是我们调用自己定义的序列化方法啦,测试代码如下:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Hadoop%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.serialize; import java.io.*; public class HadoopSerial {
public final static String fileParh = "D:\\10.Java\\IDE\\yhinzhengjieData\\hadoop.student";
public static void main(String[] args) throws IOException {
studentSerialze();
studentDeserialize();
} //定义序列化方式
public static void studentSerialze() throws IOException {
//实例化对象yzj
Student yzj = new Student();
yzj.setName("尹正杰");
yzj.setAge(18);
yzj.setIsmarry(false);
//初始化StudentWirtable,这是咱们定义的一个容器
StudentWirtable sw = new StudentWirtable();
sw.setStudent(yzj);
FileOutputStream fos = new FileOutputStream(fileParh);
DataOutputStream dos = new DataOutputStream(fos);
//进行Hadoop的序列化方式
sw.write(dos);
//别忘记释放资源哟
dos.close();
fos.close();
} //定义反序列化方式
public static void studentDeserialize() throws IOException {
//初始化intWritable
StudentWirtable sw = new StudentWirtable();
DataInputStream dis = new DataInputStream(new FileInputStream(fileParh));
sw.readFields(dis);
Student yzj = sw.getStudent();
dis.close();
System.out.println(yzj.toString()); }
} /*
以上代码执行结果如下:
Student{name='尹正杰', age=18, ismarry=false}
*/

3>.查看两种方式的序列化文件大小

   hadoop.student 文件属性如下:

  java.student 文件属性如下:

  如果一个int类型你感觉不出来hadoop序列化和java序列化的区别,那么自定义类的属性进行序列化你应该明显的看出来hadoop序列化要比java传统的序列化方式要节省空间多的多,如果这个数据换成一个PB的大小的话,估计差距就是天壤之别啦!

 

 

最新文章

  1. Tomcat:利用Apache配置反向代理、负载均衡
  2. 通过docker-machine和etcd部署docker swarm集群
  3. lua函数
  4. 在VS项目中通过GIT生成版本号作为编译版本号
  5. kuangbin_ShortPath L (POJ 2502)
  6. [Golang]Go Packages
  7. Springmvc中@RequestParam传值中文乱码解决方案(转)
  8. Jasper_dataSource_CSV data source config
  9. key转成pvf
  10. 那些年的那些事CISC和RISC发展中的纠缠
  11. Difference Between Mod_Python & Mod_Wsgi | eHow
  12. Control.Invoke和Control.BeginInvoke
  13. 论Activity及启动模式,Fragment,Service的使用以及生命周期
  14. 团队作业4——第一次项目冲刺(Alpha版本) 2017.4.23
  15. Activt工作流数据库对应表的作用
  16. java错题集
  17. Spark程序开发-环境搭建-程序编写-Debug调试-项目提交
  18. HTML 练习on方法
  19. #WEB安全基础 : HTTP协议 | 0x14 HTTP的详细安全问题
  20. 计蒜客 2019 蓝桥杯省赛 B 组模拟赛(一)

热门文章

  1. OO前三次作业简单总结
  2. 20172319 《Java程序设计教程》 第9周学习总结
  3. ubuntu16.04+cuda8.0+caffe
  4. 09_Java面向对象_第9天(类、封装)_讲义
  5. iOS成长之路-使用系统默认声音、震动
  6. 高可用集群(crmsh详解)http://www.it165.net/admin/html/201404/2869.html
  7. htop操作方法
  8. Nginx负载均衡配置与负载策略
  9. DBGrid添加行号编写笔记
  10. Dapper 事务处理