前言

  上一篇给大家介绍了Hadoop是怎么样保证数据的完整性的,并且使用Java程序来验证了会产生.crc的校验文件。这一篇给大家分享的是Hadoop的序列化!

一、序列化和反序列化概述

1.1、序列化和反序列化的定义

  1)序列化:将结构化对象转换为字节流的过程,以便在网络上传输或写入到磁盘进行永久存储的过程。
  2)反序列化:将字节流转回一系列的相反过程结构化对象。

  注意:其实流就是字节数组,我们把数据转变成一系列的字节数组(0101这样的数据)

1.2、序列化和反序列化的应用

  1)进程间的通信

  2)持久化存储

1.3、RPC序列化格式要求

  在Hadoop中,系统中多个节点上进程间的通信是通过“远程过程调用(RPC)”实现的。RPC协议将消息序列化成 二进制流后发送到远程节点,远程节点

  将二进制流反序列化为原始信息。通常情况下,RPC序列化格式如下:

    1)紧凑(compact)

      紧凑格式能充分利用网络带宽。

    2)快速(Fast)

      进程间通信形成了分布式系统的骨架,所以需要尽量减少序列化和反序列化的性能开销,这是基本..最基本的。

    3)可扩展(Extensible)

      为了满足新的需求,协议不断变化。所以控制客户端和服务器的过程中,需要直接引进相应的协议。

    4)支持互操作(Interoperable)

      对于某些系统来说,希望能支持以不同语言写的客户端与服务器交互,所以需要设计需要一种特定的格式来满足这一需求。

二、Hadoop中和虚序列化相关的接口和类

  在Java中将一个类写为可以序列化的类是实现Serializable接口

  在Hadoop中将一个类写为可以序列化的类是实现Writable接口,它是一个最顶级的接口。

1.1、Hadoop对基本数据类型的包装

  Hadoop参照JDK里面的数据类型实现了自己的数据类型,Hadoop自己实现的原理会使数据更紧凑一些,效率会高一些。序列化之后的字节数组大小会比

  JDK序列化出来的更小一些。

  所有Java基本类型的可写包装器,除了char(可以是存储在IntWritable中)。所有的都有一个get()和set()方法来检索和存储包装值。  

  

  Java中的String对应着Hadoop中的Text,Text可以存储2G的字符串大小。

1.2、Writable接口

  1)Writable接口概述

    

  2)接口中的方法

    Writable接口定义了两个方法:

      一个将其状态写到DataOutput二进制流,另一个从DataInput二进制流读取状态。

    

  3)API中Writable接口的例子:   

  1. public class MyWritable implements Writable {
  2. // Some data
  3. private int counter;
  4. private long timestamp;
  5.  
  6. public void write(DataOutput out) throws IOException {
  7. out.writeInt(counter);
  8. out.writeLong(timestamp);
  9. }
  10.  
  11. public void readFields(DataInput in) throws IOException {
  12. counter = in.readInt();
  13. timestamp = in.readLong();
  14. }
  15.  
  16. public static MyWritable read(DataInput in) throws IOException {
  17. MyWritable w = new MyWritable();
  18. w.readFields(in);
  19. return w;
  20. }
  21. }

  思考:在Java中已经有序列化和反序列化相关的类和方法,为什么Hadoop还要去自己设计一套呢?

    因为Hadoop认为Java设计的序列化和反序列化相关的类和方法性能不够好,效率太低了。所以就自己设计一套。

  4)Writable的继承关系

  

1.3、实例解释Java和Hadoop数据类型序列化的差别

  1)核心代码

  1. import java.io.ByteArrayInputStream;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.DataInputStream;
  4. import java.io.IOException;
  5. import java.io.ObjectOutputStream;
  6. import org.apache.hadoop.io.IntWritable;
  7. import org.apache.hadoop.io.Writable;
  8.  
  9. //测试使用Hadoop序列化和JDK序列化之间的区别
  10. public class SerializationCompare_0010{
  11. //Writable是Hadoop中所有数据类型的父类(父接口)。
  12. public static byte[] serialize(Writable writable) throws IOException{
  13. //这是一种编程思想,因为我们返回的是一个字节数组,所以进行了一下流的转换。
  14. ByteArrayOutputStream baos=
  15. new ByteArrayOutputStream();
  16. ObjectOutputStream oos=
  17. new ObjectOutputStream(baos);
  18. writable.write(oos);
  19. oos.close();
  20. return baos.toByteArray();
  21. }
  22.  
  23. //能序列化的一定是类类型,所以这里使用int类型的包装类
  24. public static byte[] serialize(Integer integer) throws IOException{
  25. ByteArrayOutputStream baos=
  26. new ByteArrayOutputStream();
  27. ObjectOutputStream oos=
  28. new ObjectOutputStream(baos);
  29. oos.writeInt(integer);
  30. oos.close();
  31. return baos.toByteArray();
  32. }
  33.  
  34. public static Writable deserialize(byte[] bytes) throws IOException{
  35. ByteArrayInputStream bais=
  36. new ByteArrayInputStream(bytes);
  37. DataInputStream dis=
  38. new DataInputStream(bais);
  39. IntWritable iw=new IntWritable();
  40. iw.readFields(dis);
  41. return iw;
  42. }
  43.  
  44. public static void main(String[] args) throws IOException{
  45. IntWritable iw=new IntWritable();
  46.      //hadoop也可以使用set方法传值
  47. // iw.set(300);
  48. byte[] bytes=serialize(iw);
  49. System.out.println("Hadoop:"+bytes.length);
  50. //Writable deIw=deserialize(bytes);
  51. //System.out.println("Hadoop Deserialize:"+deIw);
  52.  
  53. Integer integer=new Integer();
  54. bytes=serialize(integer);
  55. System.out.println("Java:"+bytes.length);
  56. }
  57. }

SerializationCompare_0010

  2)测试结果

    其实这里虽然是字节数组长度相同,但是在大数据中,其实是Hadoop占优势的。

1.4、在Hadoop中写一个序列化的类

  1)核心代码

  1. import java.io.ByteArrayOutputStream;
  2. import java.io.DataInput;
  3. import java.io.DataOutput;
  4. import java.io.DataOutputStream;
  5. import java.io.IOException;
  6. import java.util.ArrayList;
  7. import java.util.Arrays;
  8. import java.util.List;
  9. import org.apache.hadoop.io.BooleanWritable;
  10. import org.apache.hadoop.io.IntWritable;
  11. import org.apache.hadoop.io.Text;
  12. import org.apache.hadoop.io.Writable;
  13.  
  14. public class StudentDemo_0010{
  15. public static void main(String[] args) throws IOException{
  16. Student student=new Student();
  17. student.setId(new IntWritable());
  18. student.setName(new Text("Lance"));
  19. student.setGender(true);
  20.  
  21. ByteArrayOutputStream baos=
  22. new ByteArrayOutputStream();
  23. DataOutputStream dos=
  24. new DataOutputStream(baos);
  25. student.write(dos);
  26. byte[] data=baos.toByteArray();
  27. System.out.println(Arrays.toString(data));
  28. System.out.println(data.length);
  29.  
  30. // 将data进行反序列化?
  31. }
  32. }
  33.  
  34. class Student implements Writable{
  35. private IntWritable id;
  36. private Text name;
  37. private boolean gender;
  38. private List<Text> list=new ArrayList<>();
  39.  
  40. Student(){
  41. id=new IntWritable();
  42. name=new Text();
  43. }
  44.  
  45. /**
  46. *
  47. * @param student
  48. */
  49. Student(Student student){
  50. // 在Hadoop中这属于引用复制,完全杜绝这种现象
  51. //this.id=student.id;
  52. //this.name=student.name;
  53. // 在Hadoop中要使用属性值的复制
  54. id=new IntWritable(student.id.get());
  55. name=new Text(student.name.toString());
  56. }
  57.  
  58. @Override
  59. public void write(DataOutput out) throws IOException{
  60. id.write(out);
  61. name.write(out);
  62. BooleanWritable gender=
  63. new BooleanWritable(this.gender);
  64. gender.write(out);
  65. // 在Hadoop中序列化Java中所对应的集合的时候,
  66. // 应该现将集合的长度进行序列化,然后将集合中的
  67. // 每一个元素进行序列化
  68. int size=list.size();
  69. new IntWritable(size).write(out);
  70. for(int i=;i<size;i++){
  71. Text text=list.get(i);
  72. text.write(out);
  73. }
  74. }
  75.  
  76. @Override
  77. public void readFields(DataInput in) throws IOException{
  78. id.readFields(in);
  79. name.readFields(in);
  80. BooleanWritable bw=new BooleanWritable();
  81. bw.readFields(in);
  82. gender=bw.get();
  83. // 在反序列化集合的时候应该先反序列化集合的长度
  84. IntWritable size=new IntWritable();
  85. size.readFields(in);
  86. // 再反序列化流中所对应的结合中的每一个元素
  87. list.clear();
  88. for(int i=;i<size.get();i++){
  89. Text text=new Text();
  90. text.readFields(in);
  91. list.add(text);// 此步骤有没有问题???
  92. }
  93. }
  94.  
  95. public IntWritable getId(){
  96. return id;
  97. }
  98.  
  99. public void setId(IntWritable id){
  100. this.id=id;
  101. }
  102.  
  103. public Text getName(){
  104. return name;
  105. }
  106.  
  107. public void setName(Text name){
  108. this.name=name;
  109. }
  110.  
  111. public boolean isGender(){
  112. return gender;
  113. }
  114.  
  115. public void setGender(boolean gender){
  116. this.gender=gender;
  117. }
  118.  
  119. public List<Text> getList(){
  120. return list;
  121. }
  122.  
  123. public void setList(List<Text> list){
  124. this.list=list;
  125. }
  126. }

StudentDemo_0010

  2)测试执行:

      

      注意:" 第一部分":代表的是id,占四个字节。

         “第二部分”:代表的是name,首先5是代表字符的长度,后面是字符的ASCII码。

            注意如果将name的值改为中文,比如“二蛋子”如果是GBK编码就会占6个字节,如果是UTF-8编码就会占9个字节。  

         “第三部分”:代表的是gender,1表示ture,0表示false。

         “第四部分”:在我们list中的size,虽然这里没有数据,但是int类型的仍然会占4个字节数。

            

四、Hadoop中和比较相关的接口和类

4.1、WritableComparable<T>接口

  1)概述

    继承了两个接口

    

  2)相关方法

    继承过来的三个方法

    

4.2、RawComparator<T>接口

  1)概述

    

  2)相关方法

    除了Comparator中继承的两个方法,它自己也定义了一个方法有6个参数,这是在字节流的层面上去做比较。(第一个参数:指定字节数组,第二个参数:从哪里开始比较,第三个参数:比较多长)

    

  在考虑到使用RawComparator比较不方便,有出现了一个实现类。

4.3、WritableComparator类

  1)概述

    

  2)构造方法

    

  3)相关方法

    截取了部分

    

  介绍了上面的类和这些方法,我们Hadoop中有实现了一些既可以序列化也可以比较的类:

  

  那我们如果自定义一个类型去实现比较的功能呢?在我们前面写了一个Student的类,它具有序列化的功能,那怎么样才能有比较的功能呢?

  在Java中如果让一个类的对象具有可比较性
    1)实现Comparable接口
    2)编写独立的比较器,Comparator

  而在Hadoop如果你要实现比较的功能有:

    

  从上面的图中可以看出:

    要是一个类具有比较功能又有序列化的功能可以去实现WritableComparable接口,如果你要一个类只要有比较功能

    可以去写一个比较器用RawComparator或WritableComparator。

    总的来说最好还是去实现WritableComparable接口,因为又有序列化的功能又有比较的功能。

五、Hadoop实现序列化和比较功能

功能分析:

    

5.1、核心代码

  1. import java.io.ByteArrayInputStream;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.DataInput;
  4. import java.io.DataInputStream;
  5. import java.io.DataOutput;
  6. import java.io.DataOutputStream;
  7. import java.io.IOException;
  8. import org.apache.hadoop.io.BooleanWritable;
  9. import org.apache.hadoop.io.IntWritable;
  10. import org.apache.hadoop.io.IntWritable.Comparator;
  11. import org.apache.hadoop.io.RawComparator;
  12. import org.apache.hadoop.io.Text;
  13. import org.apache.hadoop.io.VIntWritable;
  14. import org.apache.hadoop.io.WritableComparable;
  15. import org.apache.hadoop.io.WritableUtils;
  16.  
  17. public class P00120_AccountWritable_0010{
  18. public static void main(String[] args){
  19. AccountWritable aw1=new AccountWritable();
  20. aw1.set(new IntWritable(),new Text("zyh"),new BooleanWritable(true));
  21.  
  22. AccountWritable aw2=new AccountWritable();
  23. aw2.set(new IntWritable(),new Text("zyh"),new BooleanWritable(true));
  24.  
  25. AccountWritable.DiyComparator comparator=new AccountWritable.DiyComparator();
  26. System.out.println(comparator.compare(aw1,aw2));
  27. }
  28. }
  29.  
  30. class AccountWritable
  31. implements WritableComparable<AccountWritable>{
  32.  
  33. private IntWritable code;
  34. private Text name;
  35. private BooleanWritable gender;
  36.  
  37. AccountWritable(){
  38. code=new IntWritable();
  39. name=new Text();
  40. gender=new BooleanWritable();
  41. }
  42.  
  43. // 把参数类型和类类型相同的构造器,叫复制构造器
  44. AccountWritable(AccountWritable aw){
  45. code=new IntWritable(aw.getCode().get());
  46. name=new Text(aw.getName().toString());
  47. gender=new BooleanWritable(aw.getGender().get());
  48. }
  49.  
  50. public void set(IntWritable code,Text name,BooleanWritable gender){
  51. this.code=new IntWritable(code.get());
  52. this.name=new Text(name.toString());
  53. this.gender=new BooleanWritable(gender.get());
  54. }
  55.  
  56. @Override
  57. public int compareTo(AccountWritable o){
  58. /*return this.code.compareTo(o.code)!=0?code.compareTo(o.code):
  59. (name.compareTo(o.name)!=0?name.compareTo(o.name):(this.gender.compareTo(o.gender)!=0?gender.compareTo(o.gender):0));*/
  60. int comp=this.code.compareTo(o.code);
  61. if(comp!=){
  62. return comp;
  63. }else{
  64. comp=this.name.compareTo(o.name);
  65. if(comp!=){
  66. return comp;
  67. }else{
  68. comp=this.gender.compareTo(o.gender);
  69. if(comp!=){
  70. return comp;
  71. }else{
  72. return ;
  73. }
  74. }
  75. }
  76. }
  77.  
  78. @Override
  79. public void write(DataOutput out) throws IOException{
  80. code.write(out);
  81. name.write(out);
  82. gender.write(out);
  83. }
  84.  
  85. @Override
  86. public void readFields(DataInput in) throws IOException{
  87. code.readFields(in);
  88. name.readFields(in);
  89. gender.readFields(in);
  90. }
  91.   
  92.   
  93.    //实现一个比较器
  94. static class DiyComparator
  95. implements RawComparator<AccountWritable>{
  96.  
  97. private IntWritable.Comparator ic=
  98. new Comparator();
  99. private Text.Comparator tc=
  100. new Text.Comparator();
  101. private BooleanWritable.Comparator bc=
  102. new BooleanWritable.Comparator();
  103.  
  104. @Override
  105. public int compare(byte[] b1,int s1,int l1,byte[] b2,int s2,int l2){
  106. // code被序列化后在b1和b2数组中的起始位置以及字节长度
  107. int firstLength=;
  108. int secondLength=;
  109.  
  110. int firstStart=s1;
  111. int secondStart=s2;
  112.  
  113. int firstOffset=;
  114. int secondOffset=;
  115.  
  116. // 比较字节流中的code部分
  117. int comp=ic.compare(
  118. b1,firstStart,firstLength,
  119. b2,secondStart,secondLength);
  120. if(comp!=){
  121. return comp;
  122. }else{
  123. try{
  124. // 获取记录字符串的起始位置
  125. firstStart=firstStart+firstLength;
  126. secondStart=secondStart+secondLength;
  127. // 获取记录字符串长度的VIntWritable的值的长度,被称为offset
  128. firstOffset=WritableUtils.decodeVIntSize(b1[firstStart]);
  129. secondOffset=WritableUtils.decodeVIntSize(b2[secondStart]);
  130. // 获取字符串的长度
  131. firstLength=readLengthValue(b1,firstStart);
  132. secondLength=readLengthValue(b2,secondStart);
  133. }catch(IOException e){
  134. e.printStackTrace();
  135. }
  136. // 比较字节流中的name部分
  137. comp=tc.compare(b1,firstStart+firstOffset,firstLength,b2,secondStart+secondOffset,secondLength);
  138. if(comp!=){
  139. return comp;
  140. }else{
  141. firstStart+=(firstOffset+firstLength);
  142. secondStart+=(secondOffset+secondLength);
  143. firstLength=;
  144. secondLength=;
  145. // 比较字节流中的gender部分
  146. return bc.compare(b1,firstStart,firstLength,b2,secondStart,secondLength);
  147. }
  148. }
  149. }
  150.  
  151. private int readLengthValue(
  152. byte[] bytes,int start) throws IOException{
  153. DataInputStream dis=
  154. new DataInputStream(
  155. new ByteArrayInputStream(
  156. bytes,start,WritableUtils.decodeVIntSize(bytes[start])));
  157. VIntWritable viw=new VIntWritable();
  158. viw.readFields(dis);
  159. return viw.get();
  160. }
  161.  
  162. @Override
  163. public int compare(AccountWritable o1,AccountWritable o2){
  164. ByteArrayOutputStream baos1=new ByteArrayOutputStream();
  165. DataOutputStream dos1=new DataOutputStream(baos1);
  166.  
  167. ByteArrayOutputStream baos2=new ByteArrayOutputStream();
  168. DataOutputStream dos2=new DataOutputStream(baos2);
  169.  
  170. try{
  171. o1.write(dos1);
  172. o2.write(dos2);
  173.  
  174. dos1.close();
  175. dos2.close();
  176.  
  177. byte[] b1=baos1.toByteArray();
  178. byte[] b2=baos2.toByteArray();
  179.  
  180. return compare(b1,,b1.length,b2,,b2.length);
  181. }catch(IOException e){
  182. e.printStackTrace();
  183. }
  184. return ;
  185. }
  186. }
  187.  
  188. public IntWritable getCode(){
  189. return code;
  190. }
  191.  
  192. public void setCode(IntWritable code){
  193. this.code=code;
  194. }
  195.  
  196. public Text getName(){
  197. return name;
  198. }
  199.  
  200. public void setName(Text name){
  201. this.name=name;
  202. }
  203.  
  204. public BooleanWritable getGender(){
  205. return gender;
  206. }
  207.  
  208. public void setGender(BooleanWritable gender){
  209. this.gender=gender;
  210. }
  211. }

AccountWritable

  注意如果一个类即实现了WritableComparatable接口又写了比较器,优先使用比较器。

喜欢就点个“推荐”!

Hadoop(十一)Hadoop IO之序列化与比较功能实现详解的更多相关文章

  1. Hadoop Mapreduce分区、分组、二次排序过程详解[转]

    原文地址:Hadoop Mapreduce分区.分组.二次排序过程详解[转]作者: 徐海蛟 教学用途 1.MapReduce中数据流动   (1)最简单的过程:  map - reduce   (2) ...

  2. Hadoop Mapreduce分区、分组、二次排序过程详解

    转载:http://blog.tianya.cn/m/post.jsp?postId=53271442 1.MapReduce中数据流动 (1)最简单的过程:  map - reduce (2)定制了 ...

  3. php 序列化(serialize)格式详解

    1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对序列 ...

  4. Java经典设计模式之十一种行为型模式(附实例和详解)

    Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:Java经典设计模式之 ...

  5. Java设计模式之十一种行为型模式(附实例和详解)

    Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:J设计模式之五大创建型 ...

  6. (转)Java经典设计模式(3):十一种行为型模式(附实例和详解)

    原文出处: 小宝鸽 Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:J ...

  7. 《手把手教你》系列技巧篇(四十一)-java+ selenium自动化测试 - 处理iframe -上篇(详解教程)

    1.简介 原估计宏哥这里就不对iframe这个知识点做介绍和讲解了,因为前边的窗口切换就为这种网页处理提供了思路,另一个原因就是虽然iframe很强大,但是现在很少有网站用它了.但是还是有小伙伴或者童 ...

  8. Hadoop集群(第6期)_WordCount运行详解

    1.MapReduce理论简介 1.1 MapReduce编程模型 MapReduce采用"分而治之"的思想,把对大规模数据集的操作,分发给一个主节点管理下的各个分节点共同完成,然 ...

  9. 【Java IO流】字节流和字符流详解

    字节流和字符流 对于文件必然有读和写的操作,读和写就对应了输入和输出流,流又分成字节和字符流. 1.从对文件的操作来讲,有读和写的操作——也就是输入和输出. 2.从流的流向来讲,有输入和输出之分. 3 ...

随机推荐

  1. 201521123085 《Java程序设计》第一周学习总结

    一 本周学习总结 学习了Java,又和老师见面了,这学期要好好学习Java了.Java这个东西刚刚接触很难懂,其实现在还是不懂,但是我会慢慢地努力地好好学,上机课第一次在黑色的框框弄出Hello wo ...

  2. 201521123086《JAVA程序设计》第一周学习总结

    本周学习总结 (1)初步了解java程序的运行环境,通过命令行语句编译简单的java程序 (2)使用notepad编写,cmd下进入文件夹编译程序 (3)学习使用各种快捷键补全代码 (4)能够区别jd ...

  3. 201521123093 java 第十三周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...

  4. Java课程设计—学生成绩管理系统(201521123004-林艺如)

    1.团队课程设计博客 团队课程设计博客链接 2.个人负责模块或任务说明 ①.Menu Menu.jsp 在页面中给出提示,用HTML的 MenuTeacher.jsp 利用Menu.jsp进行具体化完 ...

  5. 201521123096《Java程序设计》第十一周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 (1)互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) ...

  6. SAP中常用SM系列事务代码总结

    SM01 锁定事物 SM02 系统信息 SM04 显示在线用户 SM12 删除,显示锁对象 SM13 看update  request SM21 看下系统日志 SM30|SM31 维护table|vi ...

  7. HttpServletRequest获取URL、URI

    从Request对象中可以获取各种路径信息,以下例子: 假设请求的页面是index.jsp,项目是WebDemo,则在index.jsp中获取有关request对象的各种路径信息如下 import j ...

  8. 认识StringBuffer类

    概述: StringBuffer类是线程安全的可变字符序列 线程安全效率低 StringBuffer和String的区别 * String是一个不可变的字符序列 * StringBuffer是一个可变 ...

  9. GCD之after

    先介绍下C中的modf函数 函数名:modf 头文件:<math.h> 函数原型:double modf(double x, double *ipart) 函数用途:分解x,以得到x的整数 ...

  10. 学习率 Learning Rate

    本文从梯度学习算法的角度中看学习率对于学习算法性能的影响,以及介绍如何调整学习率的一般经验和技巧. 在机器学习中,监督式学习(Supervised Learning)通过定义一个模型,并根据训练集上的 ...