Hadoop(十一)Hadoop IO之序列化与比较功能实现详解
前言
上一篇给大家介绍了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接口的例子:
public class MyWritable implements Writable {
// Some data
private int counter;
private long timestamp; public void write(DataOutput out) throws IOException {
out.writeInt(counter);
out.writeLong(timestamp);
} public void readFields(DataInput in) throws IOException {
counter = in.readInt();
timestamp = in.readLong();
} public static MyWritable read(DataInput in) throws IOException {
MyWritable w = new MyWritable();
w.readFields(in);
return w;
}
}
思考:在Java中已经有序列化和反序列化相关的类和方法,为什么Hadoop还要去自己设计一套呢?
因为Hadoop认为Java设计的序列化和反序列化相关的类和方法性能不够好,效率太低了。所以就自己设计一套。
4)Writable的继承关系
1.3、实例解释Java和Hadoop数据类型序列化的差别
1)核心代码
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Writable; //测试使用Hadoop序列化和JDK序列化之间的区别
public class SerializationCompare_0010{
//Writable是Hadoop中所有数据类型的父类(父接口)。
public static byte[] serialize(Writable writable) throws IOException{
//这是一种编程思想,因为我们返回的是一个字节数组,所以进行了一下流的转换。
ByteArrayOutputStream baos=
new ByteArrayOutputStream();
ObjectOutputStream oos=
new ObjectOutputStream(baos);
writable.write(oos);
oos.close();
return baos.toByteArray();
} //能序列化的一定是类类型,所以这里使用int类型的包装类
public static byte[] serialize(Integer integer) throws IOException{
ByteArrayOutputStream baos=
new ByteArrayOutputStream();
ObjectOutputStream oos=
new ObjectOutputStream(baos);
oos.writeInt(integer);
oos.close();
return baos.toByteArray();
} public static Writable deserialize(byte[] bytes) throws IOException{
ByteArrayInputStream bais=
new ByteArrayInputStream(bytes);
DataInputStream dis=
new DataInputStream(bais);
IntWritable iw=new IntWritable();
iw.readFields(dis);
return iw;
} public static void main(String[] args) throws IOException{
IntWritable iw=new IntWritable();
//hadoop也可以使用set方法传值
// iw.set(300);
byte[] bytes=serialize(iw);
System.out.println("Hadoop:"+bytes.length);
//Writable deIw=deserialize(bytes);
//System.out.println("Hadoop Deserialize:"+deIw); Integer integer=new Integer();
bytes=serialize(integer);
System.out.println("Java:"+bytes.length);
}
}
SerializationCompare_0010
2)测试结果
其实这里虽然是字节数组长度相同,但是在大数据中,其实是Hadoop占优势的。
1.4、在Hadoop中写一个序列化的类
1)核心代码
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.hadoop.io.BooleanWritable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable; public class StudentDemo_0010{
public static void main(String[] args) throws IOException{
Student student=new Student();
student.setId(new IntWritable());
student.setName(new Text("Lance"));
student.setGender(true); ByteArrayOutputStream baos=
new ByteArrayOutputStream();
DataOutputStream dos=
new DataOutputStream(baos);
student.write(dos);
byte[] data=baos.toByteArray();
System.out.println(Arrays.toString(data));
System.out.println(data.length); // 将data进行反序列化?
}
} class Student implements Writable{
private IntWritable id;
private Text name;
private boolean gender;
private List<Text> list=new ArrayList<>(); Student(){
id=new IntWritable();
name=new Text();
} /**
*
* @param student
*/
Student(Student student){
// 在Hadoop中这属于引用复制,完全杜绝这种现象
//this.id=student.id;
//this.name=student.name;
// 在Hadoop中要使用属性值的复制
id=new IntWritable(student.id.get());
name=new Text(student.name.toString());
} @Override
public void write(DataOutput out) throws IOException{
id.write(out);
name.write(out);
BooleanWritable gender=
new BooleanWritable(this.gender);
gender.write(out);
// 在Hadoop中序列化Java中所对应的集合的时候,
// 应该现将集合的长度进行序列化,然后将集合中的
// 每一个元素进行序列化
int size=list.size();
new IntWritable(size).write(out);
for(int i=;i<size;i++){
Text text=list.get(i);
text.write(out);
}
} @Override
public void readFields(DataInput in) throws IOException{
id.readFields(in);
name.readFields(in);
BooleanWritable bw=new BooleanWritable();
bw.readFields(in);
gender=bw.get();
// 在反序列化集合的时候应该先反序列化集合的长度
IntWritable size=new IntWritable();
size.readFields(in);
// 再反序列化流中所对应的结合中的每一个元素
list.clear();
for(int i=;i<size.get();i++){
Text text=new Text();
text.readFields(in);
list.add(text);// 此步骤有没有问题???
}
} public IntWritable getId(){
return id;
} public void setId(IntWritable id){
this.id=id;
} public Text getName(){
return name;
} public void setName(Text name){
this.name=name;
} public boolean isGender(){
return gender;
} public void setGender(boolean gender){
this.gender=gender;
} public List<Text> getList(){
return list;
} public void setList(List<Text> list){
this.list=list;
}
}
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、核心代码
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import org.apache.hadoop.io.BooleanWritable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.IntWritable.Comparator;
import org.apache.hadoop.io.RawComparator;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.VIntWritable;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableUtils; public class P00120_AccountWritable_0010{
public static void main(String[] args){
AccountWritable aw1=new AccountWritable();
aw1.set(new IntWritable(),new Text("zyh"),new BooleanWritable(true)); AccountWritable aw2=new AccountWritable();
aw2.set(new IntWritable(),new Text("zyh"),new BooleanWritable(true)); AccountWritable.DiyComparator comparator=new AccountWritable.DiyComparator();
System.out.println(comparator.compare(aw1,aw2));
}
} class AccountWritable
implements WritableComparable<AccountWritable>{ private IntWritable code;
private Text name;
private BooleanWritable gender; AccountWritable(){
code=new IntWritable();
name=new Text();
gender=new BooleanWritable();
} // 把参数类型和类类型相同的构造器,叫复制构造器
AccountWritable(AccountWritable aw){
code=new IntWritable(aw.getCode().get());
name=new Text(aw.getName().toString());
gender=new BooleanWritable(aw.getGender().get());
} public void set(IntWritable code,Text name,BooleanWritable gender){
this.code=new IntWritable(code.get());
this.name=new Text(name.toString());
this.gender=new BooleanWritable(gender.get());
} @Override
public int compareTo(AccountWritable o){
/*return this.code.compareTo(o.code)!=0?code.compareTo(o.code):
(name.compareTo(o.name)!=0?name.compareTo(o.name):(this.gender.compareTo(o.gender)!=0?gender.compareTo(o.gender):0));*/
int comp=this.code.compareTo(o.code);
if(comp!=){
return comp;
}else{
comp=this.name.compareTo(o.name);
if(comp!=){
return comp;
}else{
comp=this.gender.compareTo(o.gender);
if(comp!=){
return comp;
}else{
return ;
}
}
}
} @Override
public void write(DataOutput out) throws IOException{
code.write(out);
name.write(out);
gender.write(out);
} @Override
public void readFields(DataInput in) throws IOException{
code.readFields(in);
name.readFields(in);
gender.readFields(in);
}
//实现一个比较器
static class DiyComparator
implements RawComparator<AccountWritable>{ private IntWritable.Comparator ic=
new Comparator();
private Text.Comparator tc=
new Text.Comparator();
private BooleanWritable.Comparator bc=
new BooleanWritable.Comparator(); @Override
public int compare(byte[] b1,int s1,int l1,byte[] b2,int s2,int l2){
// code被序列化后在b1和b2数组中的起始位置以及字节长度
int firstLength=;
int secondLength=; int firstStart=s1;
int secondStart=s2; int firstOffset=;
int secondOffset=; // 比较字节流中的code部分
int comp=ic.compare(
b1,firstStart,firstLength,
b2,secondStart,secondLength);
if(comp!=){
return comp;
}else{
try{
// 获取记录字符串的起始位置
firstStart=firstStart+firstLength;
secondStart=secondStart+secondLength;
// 获取记录字符串长度的VIntWritable的值的长度,被称为offset
firstOffset=WritableUtils.decodeVIntSize(b1[firstStart]);
secondOffset=WritableUtils.decodeVIntSize(b2[secondStart]);
// 获取字符串的长度
firstLength=readLengthValue(b1,firstStart);
secondLength=readLengthValue(b2,secondStart);
}catch(IOException e){
e.printStackTrace();
}
// 比较字节流中的name部分
comp=tc.compare(b1,firstStart+firstOffset,firstLength,b2,secondStart+secondOffset,secondLength);
if(comp!=){
return comp;
}else{
firstStart+=(firstOffset+firstLength);
secondStart+=(secondOffset+secondLength);
firstLength=;
secondLength=;
// 比较字节流中的gender部分
return bc.compare(b1,firstStart,firstLength,b2,secondStart,secondLength);
}
}
} private int readLengthValue(
byte[] bytes,int start) throws IOException{
DataInputStream dis=
new DataInputStream(
new ByteArrayInputStream(
bytes,start,WritableUtils.decodeVIntSize(bytes[start])));
VIntWritable viw=new VIntWritable();
viw.readFields(dis);
return viw.get();
} @Override
public int compare(AccountWritable o1,AccountWritable o2){
ByteArrayOutputStream baos1=new ByteArrayOutputStream();
DataOutputStream dos1=new DataOutputStream(baos1); ByteArrayOutputStream baos2=new ByteArrayOutputStream();
DataOutputStream dos2=new DataOutputStream(baos2); try{
o1.write(dos1);
o2.write(dos2); dos1.close();
dos2.close(); byte[] b1=baos1.toByteArray();
byte[] b2=baos2.toByteArray(); return compare(b1,,b1.length,b2,,b2.length);
}catch(IOException e){
e.printStackTrace();
}
return ;
}
} public IntWritable getCode(){
return code;
} public void setCode(IntWritable code){
this.code=code;
} public Text getName(){
return name;
} public void setName(Text name){
this.name=name;
} public BooleanWritable getGender(){
return gender;
} public void setGender(BooleanWritable gender){
this.gender=gender;
}
}
AccountWritable
注意如果一个类即实现了WritableComparatable接口又写了比较器,优先使用比较器。
喜欢就点个“推荐”!
Hadoop(十一)Hadoop IO之序列化与比较功能实现详解的更多相关文章
- Hadoop Mapreduce分区、分组、二次排序过程详解[转]
原文地址:Hadoop Mapreduce分区.分组.二次排序过程详解[转]作者: 徐海蛟 教学用途 1.MapReduce中数据流动 (1)最简单的过程: map - reduce (2) ...
- Hadoop Mapreduce分区、分组、二次排序过程详解
转载:http://blog.tianya.cn/m/post.jsp?postId=53271442 1.MapReduce中数据流动 (1)最简单的过程: map - reduce (2)定制了 ...
- php 序列化(serialize)格式详解
1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对序列 ...
- Java经典设计模式之十一种行为型模式(附实例和详解)
Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:Java经典设计模式之 ...
- Java设计模式之十一种行为型模式(附实例和详解)
Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:J设计模式之五大创建型 ...
- (转)Java经典设计模式(3):十一种行为型模式(附实例和详解)
原文出处: 小宝鸽 Java经典设计模式共有21中,分为三大类:创建型模式(5种).结构型模式(7种)和行为型模式(11种). 本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:J ...
- 《手把手教你》系列技巧篇(四十一)-java+ selenium自动化测试 - 处理iframe -上篇(详解教程)
1.简介 原估计宏哥这里就不对iframe这个知识点做介绍和讲解了,因为前边的窗口切换就为这种网页处理提供了思路,另一个原因就是虽然iframe很强大,但是现在很少有网站用它了.但是还是有小伙伴或者童 ...
- Hadoop集群(第6期)_WordCount运行详解
1.MapReduce理论简介 1.1 MapReduce编程模型 MapReduce采用"分而治之"的思想,把对大规模数据集的操作,分发给一个主节点管理下的各个分节点共同完成,然 ...
- 【Java IO流】字节流和字符流详解
字节流和字符流 对于文件必然有读和写的操作,读和写就对应了输入和输出流,流又分成字节和字符流. 1.从对文件的操作来讲,有读和写的操作——也就是输入和输出. 2.从流的流向来讲,有输入和输出之分. 3 ...
随机推荐
- 201521123029《Java程序设计》第六周学习总结
1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...
- 201521123103 《Java学习笔记》 第四周学习总结
一.本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. (1)多态性:相同形态,不同行为(不同的定义): (2)多态绑定:运行时能够自动地选择调用哪个 ...
- 201521123067 《Java程序设计》第3周学习总结
201521123067 <Java程序设计>第3周学习总结 1. 本周学习总结 初学面向对象,会学习到很多碎片化的概念与知识.尝试学会使用思维导图将这些碎片化的概念.知识组织起来.请使用 ...
- Java中 == 和 equals()详解
java中的数据类型分为两种: 一 .基本数据类型: byte.short.int.long.float.double.char.boolean 比较它们需要用 == ,比较的是它们的值是否相等 ...
- 从java的开始,java概述,java配置环境变量
一.java开发入门 java 概述 Java划分为三个技术平台:JavaSE(标准版,含Java基础类库),JavaEE(企业版,技术平台),JavaME(小型版,小型产品.嵌入式设备) Jav ...
- java web:在eclipse中如何创建java web 项目
Eclipse创建java web工程 eclipse版本:eclipse-jee-4.5-win32-x64 tomcat版本:apache-tomcat-7.0.63-windows-x64 jd ...
- python只re模块
while True: phone_number=input('please input your phone number:') if len(phone_number)==11 \ and pho ...
- rpm包管理
库文件 linux上,库文件是非常重要的,因为很多的软件都不是将所有的自己在需要的函数库自己写好,而是将一部分自己软件特有的库文件自己写,通用的库文件全部动态链接到公共库上去,这样不仅节省空间,同时用 ...
- java ee Servlet 开发框架分享
大家好! 这里分享一下javaEE Servlet开发框架! 1.首先是POST和GET入口以及接收处理文件 package com.sl.imps; import java.io.IOExcepti ...
- 【DDD】领域驱动设计实践 —— UI层实现
前面几篇blog主要介绍了DDD落地架构及业务建模战术,后续几篇blog会在此基础上,讲解具体的架构实现,通过完整代码demo的形式,更好地将DDD的落地方案呈现出来.本文是架构实现讲解的第一篇,主要 ...