对象的序列化(Serialization)用于将对象编码成一个字节流,以及从字节流中重新构建对象。“将一个对象编码成一个字节流”称为序列化该对象(Serializing);相反的处理过程称为反序列化(Deserializing)。
1.1 Java内建序列化机制
Java序列化机制将对象转换为连续的byte数据,这些数据可以在日后还原为原先的对象状态,该机制还能自动处理不同操作系统上的差异,在Windows系统上序列化的Java对象,可以在UNIX系统上被重建出来,不需要担心不同机器上的数据表示方法,也不需要担心字节排列次序。
在Java中,使一个类的实例可被序列化非常简单,只需要在类声明中加入implements Serializable即可。Serializable接口是一个标志,不具有任何成员函数,其定义如下:
publicinterface Serializable {
}
Serializable接口没有任何方法,所以不需要对类进行修改,Block类通过声明它实现了Serializable 接口,立即可以获得Java提供的序列化功能。代码如下:
publicclassBlockimplements Writable, Comparable<Block>, Serializable
由于序列化主要应用在与I/O相关的一些操作上,其实现是通过一对输入/输出流来实现的。如果想对某个对象执行序列化动作,可以在某种OutputStream对象的基础上创建一个对象流ObjectOutputStream对象,然后调用writeObject()就可达到目的。
writeObject()方法负责写入实现了Serializable接口对象的状态信息,输出数据将被送至该OutputStream。多个对象的序列化可以在ObjectOutputStream对象上多次调用writeObject(),分别写入这些对象。下面是序列化对象的例子:
Block block1=new Block(7806259420524417791L,39447755L,56736651L);
... ...
ByteArrayOutputStream out =new ByteArrayOutputStream();
ObjectOutputStream objOut=new ObjectOutputStream(out);
objOut.writeObject(block1);
但是,序列化以后的对象在尺寸上有点过于充实了,以Block类为例,它只包含3个长整数,但是它的序列化结果竟然有112字节。包含3个长整数的Block对象的序列化结果如下:
-84, -19, 0, 5, 115, 114, 0, 23, 111, 114, 103, 46, 115, 101, 97, 110, 100, 101, 110, 103, 46, 116, 101, 115, 116, 46, 66, 108, 111, 99, 107, 40, -7, 56, 46, 72, 64, -69, 45, 2, 0, 3, 74, 0, 7, 98, 108, 111, 99, 107, 73, 100, 74, 0, 16, 103, 101, 110, 101, 114, 97, 116, 105, 111, 110, 115, 83, 116, 97, 109, 112, 74, 0, 8, 110, 117, 109, 66, 121, 116, 101, 115, 120, 112, 108, 85, 103, -107, 104, -25, -110, -1, 0, 0, 0, 0, 3, 97, -69, -117, 0, 0, 0, 0, 2, 89, -20, -53
1.2 Hadoop序列化机制
和Java序列化机制不同(在对象流ObjectOutputStream对象上调用writeObject()方法),Hadoop的序列化机制通过调用对象的write()方法(它带有一个类型为DataOutput的参数),将对象序列化到流中。反序列化的过程也是类似,通过对象的readFields(),从流中读取数据。值得一提的是,Java序列化机制中,反序列化过程会不断地创建新的对象,但在Hadoop的序列化机制的反序列化过程中,用户可以复用对象,这减少了Java对象的分配和回收,提高了应用的效率。
public static void main(String[] args) {
try {
Block block1 = new Block(1L,2L,3L);
... ...
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream();
block1.write(dout);
dout.close();
... ...
}
... ...
}
由于Block对象序列化时只输出了3个长整数,block1的序列化结果一共有24字节。
1.3 Hadoop Writable机制
Hadoop引入org.apache.hadoop.io.Writable接口,作为所有可序列化对象必须实现的接口,在eclipse开发工具里看到的大纲视图如下:
和java.io.Serializable不同,Writable接口不是一个说明性接口,它包含两个方法:
publicinterface Writable {
/**
* Serialize the fields of this object to <code>out</code>.
* @param out <code>DataOuput</code> to serialize this object into.
* @throws IOException
*/
void write(DataOutput out) throws IOException;
/**
* Deserialize the fields of this object from <code>in</code>.
* For efficiency, implementations should attempt to re-use storage in the
* existing object where possible.</p>
* @param in <code>DataInput</code> to deseriablize this object from.
* @throws IOException
*/
void readFields(DataInput in) throws IOException;
}
Writable.write(DataOutput out)方法用于将对象写入二进制的DataOutput中,反序列化的过程由readFields(DataInput in)从DataInput流中读取状态完成。下面是一个例子:
public class Block {
private long blockId;
private long numBytes;
private long generationsStamp;
public void write(DataOutput out) throws IOException {
out.writeLong(blockId);
out.writeLong(numBytes);
out.writeLong(generationsStamp);
}
public void readFields(DataInput in) throws IOException {
this.blockId = in.readLong();
this.numBytes = in.readLong();
this.generationsStamp = in.readLong();
if (numBytes < 0 ) {
throw new IOException("Unexpected block size:" + numBytes);
}
}
}
Hadoop序列化机制中还包括另外几个重要接口:WritableComparable、RawComparator和WritableComparator。
Comparable是一个对象本身就已经支持自比较所需要实现的接口(如Integer自己就可以完成比较大小操作),实现Comparable接口的方法compareTo(),通过传入要比较的对象即可进行比较。
而Comparator是一个专用的比较器,可以完成两个对象之间大小的比较。实现Comparator接口的compare()方法,通过传入需要比较的两个对象来实现对两个对象之间大小的比较。
1.4 典型的Writable类详解
1.4.1 Java基本类型的Writable封装
Java基本类型对应的Writable封装如下表:
Java基本类型 |
Writable |
布尔型(Boolean) |
BooleanWritable |
字节型(byte) |
ByteWritable |
整型(int) |
IntWritable VIntWritable |
浮点型(float) |
FloatWritable |
长整型(long) |
LongWritable VLongWritable |
双精度浮点型(double) |
DoubleWritable |
下面以VIntWritable为例,代码如下:
publicclass VIntWritable implements WritableComparable {
privateintvalue;
public VIntWritable() {}
public VIntWritable(intvalue) { set(value); }
/** Set the value of this VIntWritable. */
publicvoid set(intvalue) { this.value = value; }
/** Return the value of this VIntWritable. */
publicint get() { returnvalue; }
publicvoid readFields(DataInput in) throws IOException {
value = WritableUtils.readVInt(in);
}
publicvoid write(DataOutput out) throws IOException {
WritableUtils.writeVInt(out, value);
}
/** Compares two VIntWritables. */
publicint compareTo(Object o) {
intthisValue = this.value;
intthatValue = ((VIntWritable)o).value;
return (thisValue < thatValue ? -1 : (thisValue == thatValue ? 0 : 1));
}
}
VIntWritable是通过调用Writable工具类中提供的readVInt()和writeVInt()读/写数据。
1.4.2 ObjectWritable类的实现
针对类实例,ObjectWritable提供了一个封装。相关代码如下:
publicclass ObjectWritable implements Writable, Configurable {
private Class declaredClass;
private Object instance;
private Configuration conf;
public ObjectWritable() {}
public ObjectWritable(Object instance) {
set(instance);
}
public ObjectWritable(Class declaredClass, Object instance) {
this.declaredClass = declaredClass;
this.instance = instance;
}
/** Return the instance, or null if none. */
public Object get() { returninstance; }
/** Return the class this is meant to be. */
public Class getDeclaredClass() { returndeclaredClass; }
/** Reset the instance. */
publicvoid set(Object instance) {
this.declaredClass = instance.getClass();
this.instance = instance;
}
publicvoid readFields(DataInput in) throws IOException {
readObject(in, this, this.conf);
}
publicvoid write(DataOutput out) throws IOException {
writeObject(out, instance, declaredClass, conf);
}
/** Write a {@link Writable}, {@link String}, primitive type, or an array of
* the preceding. */
publicstaticvoid writeObject(DataOutput out, Object instance,
Class declaredClass,
Configuration conf) throws IOException {
if (instance == null) { // null
instance = new NullInstance(declaredClass, conf);
declaredClass = Writable.class;
}
UTF8.writeString(out, declaredClass.getName()); // always write declared
if (declaredClass.isArray()) { // array
intlength = Array.getLength(instance);
out.writeInt(length);
for (inti = 0; i < length; i++) {
writeObject(out, Array.get(instance, i),
declaredClass.getComponentType(), conf);
}
} elseif (declaredClass == String.class) { // String
UTF8.writeString(out, (String)instance);
} elseif (declaredClass.isPrimitive()) { // primitive type
if (declaredClass == Boolean.TYPE) { // boolean
out.writeBoolean(((Boolean)instance).booleanValue());
} elseif (declaredClass == Character.TYPE) { // char
out.writeChar(((Character)instance).charValue());
} elseif (declaredClass == Byte.TYPE) { // byte
out.writeByte(((Byte)instance).byteValue());
} elseif (declaredClass == Short.TYPE) { // short
out.writeShort(((Short)instance).shortValue());
} elseif (declaredClass == Integer.TYPE) { // int
out.writeInt(((Integer)instance).intValue());
} elseif (declaredClass == Long.TYPE) { // long
out.writeLong(((Long)instance).longValue());
} elseif (declaredClass == Float.TYPE) { // float
out.writeFloat(((Float)instance).floatValue());
} elseif (declaredClass == Double.TYPE) { // double
out.writeDouble(((Double)instance).doubleValue());
} elseif (declaredClass == Void.TYPE) { // void
} else {
thrownew IllegalArgumentException("Not a primitive: "+declaredClass);
}
} elseif (declaredClass.isEnum()) { // enum
UTF8.writeString(out, ((Enum)instance).name());
} elseif (Writable.class.isAssignableFrom(declaredClass)) { // Writable
UTF8.writeString(out, instance.getClass().getName());
((Writable)instance).write(out);
} else {
thrownew IOException("Can't write: "+instance+" as "+declaredClass);
}
}
/** Read a {@link Writable}, {@link String}, primitive type, or an array of
* the preceding. */
publicstatic Object readObject(DataInput in, Configuration conf)
throws IOException {
return readObject(in, null, conf);
}
/** Read a {@link Writable}, {@link String}, primitive type, or an array of
* the preceding. */
@SuppressWarnings("unchecked")
publicstatic Object readObject(DataInput in, ObjectWritable objectWritable, Configuration conf)
throws IOException {
String className = UTF8.readString(in);
Class<?> declaredClass = PRIMITIVE_NAMES.get(className);
if (declaredClass == null) {
try {
declaredClass = conf.getClassByName(className);
} catch (ClassNotFoundException e) {
thrownew RuntimeException("readObject can't find class " + className, e);
}
}
Object instance;
if (declaredClass.isPrimitive()) { // primitive types
if (declaredClass == Boolean.TYPE) { // boolean
instance = Boolean.valueOf(in.readBoolean());
} elseif (declaredClass == Character.TYPE) { // char
instance = Character.valueOf(in.readChar());
} elseif (declaredClass == Byte.TYPE) { // byte
instance = Byte.valueOf(in.readByte());
} elseif (declaredClass == Short.TYPE) { // short
instance = Short.valueOf(in.readShort());
} elseif (declaredClass == Integer.TYPE) { // int
instance = Integer.valueOf(in.readInt());
} elseif (declaredClass == Long.TYPE) { // long
instance = Long.valueOf(in.readLong());
} elseif (declaredClass == Float.TYPE) { // float
instance = Float.valueOf(in.readFloat());
} elseif (declaredClass == Double.TYPE) { // double
instance = Double.valueOf(in.readDouble());
} elseif (declaredClass == Void.TYPE) { // void
instance = null;
} else {
thrownew IllegalArgumentException("Not a primitive: "+declaredClass);
}
} elseif (declaredClass.isArray()) { // array
intlength = in.readInt();
instance = Array.newInstance(declaredClass.getComponentType(), length);
for (inti = 0; i < length; i++) {
Array.set(instance, i, readObject(in, conf));
}
} elseif (declaredClass == String.class) { // String
instance = UTF8.readString(in);
} elseif (declaredClass.isEnum()) { // enum
instance = Enum.valueOf((Class<? extends Enum>) declaredClass, UTF8.readString(in));
} else { // Writable
Class instanceClass = null;
String str = "";
try {
str = UTF8.readString(in);
instanceClass = conf.getClassByName(str);
} catch (ClassNotFoundException e) {
thrownew RuntimeException("readObject can't find class " + str, e);
}
Writable writable = WritableFactories.newInstance(instanceClass, conf);
writable.readFields(in);
instance = writable;
if (instanceClass == NullInstance.class) { // null
declaredClass = ((NullInstance)instance).declaredClass;
instance = null;
}
}
if (objectWritable != null) { // store values
objectWritable.declaredClass = declaredClass;
objectWritable.instance = instance;
}
returninstance;
}
... ...
}
通过readFields方法反序列化一个object。而如果DataInput中传过来的是Writable 类型,则会在readObject再去调用readFields方法(writable.readFields(in)),直到DataInput中传递 的是非Writable 类型,就这样递归的反序列化DataInput中的Writable对象。
readObject()方法依赖于WritableFactories类。WritableFactories类允许非公有的Writable子类定义一个对象工厂,由该工厂创建Writable对象。相关代码如下:
publicclass WritableFactories {
privatestaticfinal HashMap<Class, WritableFactory> CLASS_TO_FACTORY =
new HashMap<Class, WritableFactory>();
private WritableFactories() {} // singleton
/** Define a factory for a class. */
publicstaticsynchronizedvoid setFactory(Class c, WritableFactory factory) {
CLASS_TO_FACTORY.put(c, factory);
}
/** Define a factory for a class. */
publicstaticsynchronized WritableFactory getFactory(Class c) {
returnCLASS_TO_FACTORY.get(c);
}
/** Create a new instance of a class with a defined factory. */
publicstatic Writable newInstance(Class<? extends Writable> c, Configuration conf) {
WritableFactory factory = WritableFactories.getFactory(c);
if (factory != null) {
Writable result = factory.newInstance();
if (resultinstanceof Configurable) {
((Configurable) result).setConf(conf);
}
returnresult;
} else {
return ReflectionUtils.newInstance(c, conf);
}
}
/** Create a new instance of a class with a defined factory. */
publicstatic Writable newInstance(Class<? extends Writable> c) {
return newInstance(c, null);
}
}
WritableFacories.newInstance()方法根据输入的类型查找对应的WritableFactory工厂对象,然后调用该对象的newInstance()创建对象,如果该对象是可配置的,newInstance()还会通过对象的setConf()方法配置对象。
相关推荐
【尚硅谷大数据技术之Hadoop(MapReduce)1】深入解析MapReduce MapReduce是Google提出的一种用于处理和生成大规模数据集的编程模型,被广泛应用于大数据处理领域。Hadoop将其作为核心组件,实现了分布式计算的功能...
总的来说,Hadoop的序列化机制是其高效处理大数据的关键组成部分,通过自定义Bean对象实现Writable接口,我们能够灵活地处理各种复杂数据类型,并在网络和磁盘间进行高效的数据交换。理解并掌握这一机制对于开发...
同时,通过Avro这样的数据序列化系统,可以实现数据高效地在Hadoop组件之间进行传输。 7. 云计算技术 文档提到的云计算技术,虽然没有深入展开,但与Hadoop结合使用是非常重要的。比如,Amazon S3作为云存储服务,...
在当今信息化社会,大数据已成为企业竞争力的重要组成部分,而Hadoop作为处理海量数据的基石,其重要性不言而喻。本资料旨在深入解析Hadoop的核心概念、架构以及实际应用,帮助读者掌握分布式计算的基础知识,并能够...
NameNode将所有metadata存储在内存中,启动时才从硬盘读取序列化的metadata。Client向NameNode发起读写请求,NameNode基于内存中的信息响应。 2. 文件写入过程: - Client向NameNode请求创建文件。 - NameNode...
Hadoop 是一个能够对大量数据进行分布式处理的软件框架。但是 Hadoop 是以一种可靠、高效、可伸缩的方式进行处理的。Hadoop 是可靠的,因为它假设计算元素和存储会失败,因此它维护多个工作数据副本,确保能够针对...
Common模块提供了诸如文件系统、远程过程调用(RPC)和序列化库等基础服务,支持整个Hadoop生态系统的运行。HDFS(Hadoop Distributed File System)是分布式文件系统,它允许数据在多台服务器上分布式存储,以支持...
Hadoop 是一个能够对大量数据进行分布式处理的软件框架。但是 Hadoop 是以一种可靠、高效、可伸缩的方式进行处理的。Hadoop 是可靠的,因为它假设计算元素和存储会失败,因此它维护多个工作数据副本,确保能够针对...
**大数据技术:Hadoop 框架详细介绍** 在当今信息化社会,海量的数据正在不断涌现,传统的数据处理方式已经无法满足需求。此时,大数据技术应运而生,其中Hadoop框架作为开源的分布式计算平台,成为了大数据处理的...
通过这篇文章,我们可以了解到MapReduce的定义、优缺点、核心思想、进程、编程规范等内容,也了解到Hadoop序列化的重要性和基本概念。从中我们可以看到MapReduce是Hadoop核心模块之一,广泛应用于大数据处理和分析...
* 序列化:Hadoop支持多种序列化格式,如Avro、Protocol Buffers等。 大数据Hadoop平台集成实施服务解决方案 大数据Hadoop平台集成实施服务解决方案是基于Hadoop技术的数据存储和分析处理平台的实施方案,该方案...
在分布式计算领域,Hadoop是一个不可或缺的名字,它提供了一整套处理大数据的框架。在Hadoop中,远程过程调用(RPC)是核心组件之一,它使得节点间的通信变得高效且可靠。本文将深入探讨Hadoop的RPC机制,解析其工作...
7. **序列化框架**:序列化是数据交换的关键,课程详细阐述了Hadoop中可替换的序列化框架,包括Writable接口、Avro和其他序列化技术如Protocol Buffers,强调了Avro的灵活性和跨语言兼容性。 8. **MapReduce进阶**...
- 设计对象序列化框架。 6. Hadoop的主要组件和概念 - Hadoop MapReduce:一个用于大规模数据集处理的编程模型和相关实现。 - Hadoop分布式文件系统(HDFS):一个分布式文件存储系统,它存储数据跨越多台机器。...
例如,Hadoop 的数据块大小设置、Zookeeper 的会话超时机制、HBase 的 Region 分裂、Hive 的分区表设计、Spark 的容错机制、Kafka 的生产者消费者模型、Flink 的状态持久化和ClickHouse 的索引策略等都是常见的面试...
在Hadoop中,自定义的序列化机制如Writable接口和Protocol Buffers等,能够优化数据传输和存储效率,适应大数据处理的性能需求。通过这些序列化工具,开发者可以高效地处理MapReduce任务中的数据交换,确保整个计算...
它包括Map、Reduce和Driver三个部分,使用Hadoop的Writable接口进行数据序列化,例如BooleanWritable、IntWritable等。在实际操作中,需要编写Mapper将文本拆分成单词,Reducer则对单词计数并输出。 **环境准备与...
论文中提到的“大数据框架平台的嵌接”可能是指将Matlab计算结果与Hadoop、Spark等大数据处理框架相结合。通过这种方式,可以实现数据的分布式存储和并行计算,提高整体的处理效率和数据分析的实时性。 具体实施时...