《hadoop权威指南》学习笔记-hadoop I/O之序列化

来源:互联网 发布:java视频教程2016 编辑:程序博客网 时间:2024/06/08 18:16

这一部分内容是序列化,关于序列化的内容,书上理论讲的多,实践起来多是利用JUnit进行测试。所以我还花费精力弄了一番JUnit,在eclipse下进行了测试,由于才疏学浅,命令行下搞了半天都不行,希望有大神能指导一下。Eclipse下步骤比较简单,只是虚拟机下的eclipse卡的让人受不了。

首先在eclipse下建立一个mapreduce的工程,然后创建包,把相关的类都添加到这个包下面(注意是相关包,有的包在/classes目录下,有的是在/test-classes目录下)。然后给每个类添加包名(当然eclipse会提示你的)。而且还应该在该工程下添加几个jar包,就是hamcrest-core-1.3.jarhamcrest-library-1.3.jar。很多文章都没有提hamcrest-library-1.3.jar这个,因为不添加这个包会找不到org.hamcrest.Matchers,所以告诫大家一定要添加这个包。这是添加包的截图:

图片1


图片2


先随便测试一个,点击该.java文件右键,run as -> JUnit Test,测试成功。

测试图1


测试图2


预备工作做好后就开始正题。

一、书中先讲了序列化的特点,很透彻,在此就不赘述。然后介绍到Writable这个接口,一定要注意这是个接口,这个接口定义了两个方法,write用于序列化,readFields用于反序列化。接着又讲到一个类IntWritable,这个类和Writable之间不是直接的关系,IntWritable是继承自WritableComparable接口,而WritableComparable又继承了WritableComparator这两个接口。所以IntWritable既实现了序列化操作也实现了比较操作。这里提一下比较操作的意义,我们知道map把输出提交给reduce的过程中间还有一个排序的操作,比较的作用就在这个时候体现。

再下边就是介绍WritableComparator,和RawComparator,有汉化版的这部分翻译的很渣,所以我直接上网看了一下这几个类(或接口)之间的关系。WritableComparator是一个继承了RawComparator接口的类,WritableComparator是给WritableComparable服务的,也就是说用于比较两个实例对象的,因为IntWritable继承自WritableComparable

如此看来,WritableComparator算是一个处理工具用来处理**Wratable之间的比较。

先前已经运行过IntWritableTest,所以我们这一次将这部分代码改一下,改为assertThat(comparator.compare(w2, w1), greaterThan(0));

这样再测试,果然出现了错误。

测试错误1


至于比较的方法具体是如何实现的,我也没有研究,以后读源码应该会知道的。

二、下面这部分讲的是hadoop自带的一些Writable类。



(1)定长格式(IntWritableLongWritable)和变长格式(VIntWritableVLongWritable)。我们直接讲他们的应用场合,如果数值比较小(在-127127之间),就用变长格式,而且如果大多数数值变量分布不均匀也最好用变长格式,还有VIntWritableVLongWritable是可以直接转换的。如果数值大体分布均匀,就用定长格式。

(2)Text类和String很相似,但两者是有不同的。Text不能对字节数超过32767的字符串进行编码;Text的索引是根据编码后字节序列的位置实现的,而String的索引是根据Java Char的编码单元实现的。不过对于ASCII码来说是一样的。所以用ASCII码,这两个类的索引差别是没有的。但是如果使用Unicode编码,这个差异就很明显了。


看书上提供的这段代码:

public class StringTextComparisonTest {@Testpublic void string() throws UnsupportedEncodingException {String s = "\u0041\u00DF\u6771\uD801\uDC00";assertThat(s.length(), is(5));assertThat(s.getBytes("UTF-8").length, is(10));assertThat(s.indexOf("\u0041"), is(0));assertThat(s.indexOf("\u00DF"), is(1));assertThat(s.indexOf("\u6771"), is(2));assertThat(s.indexOf("\uD801\uDC00"), is(3));assertThat(s.charAt(0), is('\u0041'));assertThat(s.charAt(1), is('\u00DF'));assertThat(s.charAt(2), is('\u6771'));assertThat(s.charAt(3), is('\uD801'));assertThat(s.charAt(4), is('\uDC00'));assertThat(s.codePointAt(0), is(0x0041));assertThat(s.codePointAt(1), is(0x00DF));assertThat(s.codePointAt(2), is(0x6771));assertThat(s.codePointAt(3), is(0x10400));}@Testpublic void text() {Text t = new Text("\u0041\u00DF\u6771\uD801\uDC00");assertThat(t.getLength(), is(10));assertThat(t.find("\u0041"), is(0));assertThat(t.find("\u00DF"), is(1));assertThat(t.find("\u6771"), is(3));assertThat(t.find("\uD801\uDC00"), is(6));assertThat(t.charAt(0), is(0x0041));assertThat(t.charAt(1), is(0x00DF));assertThat(t.charAt(3), is(0x6771));assertThat(t.charAt(6), is(0x10400));}}


String类型的索引地址就是字符的相应位置,而Text类型的索引地址则是字节的位置(41一个字节,c39f两个字节,e69db1三个字节,f0909080四个字节),所以,第一个字符索引位置是0,第二个字符索引位置是1,第三个字符索引位置是3,第四个字符索引位置是6Text类的find方法返回的是字节偏移量。与Text类的indexOf相对应。

TextString的最后一个区别就是,Text是可变的,而String是不可变的。

(3)BytesWritable是对二进制数组的封装,前四个字节表示数据的长度,后面每两个字节就对应一个具体数据。该类也是可变的。而且和Text一样,getLength()getBytes().getLength()所表示的长度很可能不同,getLength()的长度才是有效字符的长度。

NullWritable相当于null,序列化长度为0,只是充当占位符。

ObjectWritable多用于多类型数组

三、Writable集合类

ArrayWritable 和 TwoDArrayWritable是对Writable的数组和二维数组的实现。但他们中的元素类型必须相同。相当于一次将多个同类型的数据序列化。

MapWritable相当于利用键值对来序列化,也就是说可以添加不同类型的数据,只要不同的类型对应不同的键值就可以了。

实现自定义Writable类型

这部分内容就是自己构建一个Writable,书上有源代码,没什么好讲的,只是在Comparator这段代码这里有个地方理解不透。

代码:

public static class Comparator extends WritableComparator {private static final Text.Comparator TEXT_COMPARATOR = new Text.Comparator();public Comparator() {super(TextPair.class);}@Overridepublic int compare(byte[] b1, int s1, int l1,byte[] b2, int s2, int l2) {try {int firstL1 = WritableUtils.decodeVIntSize(b1[s1]) + readVInt(b1, s1);int firstL2 = WritableUtils.decodeVIntSize(b2[s2]) + readVInt(b2, s2);int cmp = TEXT_COMPARATOR.compare(b1, s1, firstL1, b2, s2, firstL2);if (cmp != 0) {return cmp;}return TEXT_COMPARATOR.compare(b1, s1 + firstL1, l1 - firstL1,b2, s2 + firstL2, l2 - firstL2);} catch (IOException e) {throw new IllegalArgumentException(e);}}}


int firstL1 = WritableUtils.decodeVIntSize(b1[s1]) + readVInt(b1, s1);

这句是要求出Byte1的长度,具体原因请参照这篇博客,说的很详细。

后边介绍的这个FirstComparator就更简单了,他只是比较了TextPair的第一个Text,工作量减少了。


原创粉丝点击