原文链接 http://blog.csdn.net/lastsweetop/article/details/9249411

所有源码在github上,https://github.com/lastsweetop/styhadoop

简介

在hadoop中,Writable的实现类是个庞大的家族,我们在这里简单的介绍一下常用来做序列化的一部分。

java原生类型

除char类型以外,所有的原生类型都有对应的Writable类,并且通过get和set方法可以他们的值。
IntWritable和LongWritable还有对应的变长VIntWritable和VLongWritable类。
固定长度还是变长的选用类似与数据库中的char或者vchar,在这里就不赘述了。

Text类型

Text类型使用变长int型存储长度,所以Text类型的最大存储为2G.
Text类型采用标准的utf-8编码,所以与其他文本工具可以非常好的交互,但要注意的是,这样的话就和java的String类型差别就很多了。

检索的不同

Text的chatAt返回的是一个整型,及utf-8编码后的数字,而不是象String那样的unicode编码的char类型。
[java] view plaincopy
  1. @Test
  2. public void testTextIndex(){
  3. Text text=new Text("hadoop");
  4. Assert.assertEquals(text.getLength(), 6);
  5. Assert.assertEquals(text.getBytes().length, 6);
  6. Assert.assertEquals(text.charAt(2),(int)'d');
  7. Assert.assertEquals("Out of bounds",text.charAt(100),-1);
  8. }

Text还有个find方法,类似String里indexOf方法

[java] view plaincopy
  1. @Test
  2. public void testTextFind() {
  3. Text text = new Text("hadoop");
  4. Assert.assertEquals("find a substring",text.find("do"),2);
  5. Assert.assertEquals("Find first 'o'",text.find("o"),3);
  6. Assert.assertEquals("Find 'o' from position 4 or later",text.find("o",4),4);
  7. Assert.assertEquals("No match",text.find("pig"),-1);
  8. }

Unicode的不同

当uft-8编码后的字节大于两个时,Text和String的区别就会更清晰,因为String是按照unicode的char计算,而Text是按照字节计算。
我们来看下1到4个字节的不同的unicode字符
4个unicode分别占用1到4个字节,u+10400在java的unicode字符重占用两个char,前三个字符分别占用1个char
我们通过代码来看下String和Text的不同
[java] view plaincopy
  1. @Test
  2. public void string() throws UnsupportedEncodingException {
  3. String str = "\u0041\u00DF\u6771\uD801\uDC00";
  4. Assert.assertEquals(str.length(), 5);
  5. Assert.assertEquals(str.getBytes("UTF-8").length, 10);
  6. Assert.assertEquals(str.indexOf("\u0041"), 0);
  7. Assert.assertEquals(str.indexOf("\u00DF"), 1);
  8. Assert.assertEquals(str.indexOf("\u6771"), 2);
  9. Assert.assertEquals(str.indexOf("\uD801\uDC00"), 3);
  10. Assert.assertEquals(str.charAt(0), '\u0041');
  11. Assert.assertEquals(str.charAt(1), '\u00DF');
  12. Assert.assertEquals(str.charAt(2), '\u6771');
  13. Assert.assertEquals(str.charAt(3), '\uD801');
  14. Assert.assertEquals(str.charAt(4), '\uDC00');
  15. Assert.assertEquals(str.codePointAt(0), 0x0041);
  16. Assert.assertEquals(str.codePointAt(1), 0x00DF);
  17. Assert.assertEquals(str.codePointAt(2), 0x6771);
  18. Assert.assertEquals(str.codePointAt(3), 0x10400);
  19. }
  20. @Test
  21. public void text() {
  22. Text text = new Text("\u0041\u00DF\u6771\uD801\uDC00");
  23. Assert.assertEquals(text.getLength(), 10);
  24. Assert.assertEquals(text.find("\u0041"), 0);
  25. Assert.assertEquals(text.find("\u00DF"), 1);
  26. Assert.assertEquals(text.find("\u6771"), 3);
  27. Assert.assertEquals(text.find("\uD801\uDC00"), 6);
  28. Assert.assertEquals(text.charAt(0), 0x0041);
  29. Assert.assertEquals(text.charAt(1), 0x00DF);
  30. Assert.assertEquals(text.charAt(3), 0x6771);
  31. Assert.assertEquals(text.charAt(6), 0x10400);
  32. }

这样一比较就很明显了。

1.String的length()方法返回的是char的数量,Text的getLength()方法返回的是字节的数量。
2.String的indexOf()方法返回的是以char为单元的偏移量,Text的find()方法返回的是以字节为单位的偏移量。
3.String的charAt()方法不是返回的整个unicode字符,而是返回的是java中的char字符
4.String的codePointAt()和Text的charAt方法比较类似,不过要注意,前者是按char的偏移量,后者是字节的偏移量

Text的迭代

在Text中对unicode字符的迭代是相当复杂的,因为与unicode所占的字节数有关,不能简单的使用index的增长来确定。首先要把Text对象使用ByteBuffer进行封装,然后再调用Text的静态方法bytesToCodePoint对ByteBuffer进行轮询返回unicode字符的code point。看一下示例代码:
[java] view plaincopy
  1. package com.sweetop.styhadoop;
  2. import org.apache.hadoop.io.Text;
  3. import java.nio.ByteBuffer;
  4. /**
  5. * Created with IntelliJ IDEA.
  6. * User: lastsweetop
  7. * Date: 13-7-9
  8. * Time: 下午5:00
  9. * To change this template use File | Settings | File Templates.
  10. */
  11. public class TextIterator {
  12. public static void main(String[] args) {
  13. Text text = new Text("\u0041\u00DF\u6771\uD801\udc00");
  14. ByteBuffer buffer = ByteBuffer.wrap(text.getBytes(), 0, text.getLength());
  15. int cp;
  16. while (buffer.hasRemaining() && (cp = Text.bytesToCodePoint(buffer)) != -1) {
  17. System.out.println(Integer.toHexString(cp));
  18. }
  19. }
  20. }

Text的修改

除了NullWritable是不可更改外,其他类型的Writable都是可以修改的。你可以通过Text的set方法去修改去修改重用这个实例。
[java] view plaincopy
  1. @Test
  2. public void testTextMutability() {
  3. Text text = new Text("hadoop");
  4. text.set("pig");
  5. Assert.assertEquals(text.getLength(), 3);
  6. Assert.assertEquals(text.getBytes().length, 3);
  7. }

但要注意的就是,在某些情况下Text的getBytes方法返回的字节数组的长度和Text的getLength方法返回的长度不一致。因此,在调用getBytes()方法的同时最好也调用一下getLength方法,这样你就知道在字节数组里有多少有效的字符。

[java] view plaincopy
  1. @Test
  2. public void testTextMutability2() {
  3. Text text = new Text("hadoop");
  4. text.set(new Text("pig"));
  5. Assert.assertEquals(text.getLength(),3);
  6. Assert.assertEquals(text.getBytes().length,6);
  7. }

BytesWritable类型

ByteWritable类型是一个二进制数组的封装类型,序列化格式是以一个4字节的整数(这点与Text不同,Text是以变长int开头)开始表明字节数组的长度,然后接下来就是数组本身。看下示例:

[java] view plaincopy
  1. @Test
  2. public void testByteWritableSerilizedFromat() throws IOException {
  3. BytesWritable bytesWritable=new BytesWritable(new byte[]{3,5});
  4. byte[] bytes=SerializeUtils.serialize(bytesWritable);
  5. Assert.assertEquals(StringUtils.byteToHexString(bytes),"000000020305");
  6. }

和Text一样,ByteWritable也可以通过set方法修改,getLength返回的大小是真实大小,而getBytes返回的大小确不是。

[java] view plaincopy
  1. <span style="white-space:pre">  </span>bytesWritable.setCapacity(11);
  2. bytesWritable.setSize(4);
  3. Assert.assertEquals(4,bytesWritable.getLength());
  4. Assert.assertEquals(11,bytesWritable.getBytes().length);

NullWritable类型

NullWritable是一个非常特殊的Writable类型,序列化不包含任何字符,仅仅相当于个占位符。你在使用mapreduce时,key或者value在无需使用时,可以定义为NullWritable。
[java] view plaincopy
  1. package com.sweetop.styhadoop;
  2. import org.apache.hadoop.io.NullWritable;
  3. import org.apache.hadoop.util.StringUtils;
  4. import java.io.IOException;
  5. /**
  6. * Created with IntelliJ IDEA.
  7. * User: lastsweetop
  8. * Date: 13-7-16
  9. * Time: 下午9:23
  10. * To change this template use File | Settings | File Templates.
  11. */
  12. public class TestNullWritable {
  13. public static void main(String[] args) throws IOException {
  14. NullWritable nullWritable=NullWritable.get();
  15. System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(nullWritable)));
  16. }
  17. }

ObjectWritable类型

ObjectWritable是其他类型的封装类,包括java原生类型,String,enum,Writable,null等,或者这些类型构成的数组。当你的一个field有多种类型时,ObjectWritable类型的用处就发挥出来了,不过有个不好的地方就是占用的空间太大,即使你存一个字母,因为它需要保存封装前的类型,我们来看瞎示例:

[java] view plaincopy
  1. package com.sweetop.styhadoop;
  2. import org.apache.hadoop.io.ObjectWritable;
  3. import org.apache.hadoop.io.Text;
  4. import org.apache.hadoop.util.StringUtils;
  5. import java.io.IOException;
  6. /**
  7. * Created with IntelliJ IDEA.
  8. * User: lastsweetop
  9. * Date: 13-7-17
  10. * Time: 上午9:14
  11. * To change this template use File | Settings | File Templates.
  12. */
  13. public class TestObjectWritable {
  14. public static void main(String[] args) throws IOException {
  15. Text text=new Text("\u0041");
  16. ObjectWritable objectWritable=new ObjectWritable(text);
  17. System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(objectWritable)));
  18. }
  19. }

仅仅是保存一个字母,那么看下它序列化后的结果是什么:

[java] view plaincopy
  1. 00196f72672e6170616368652e6861646f6f702e696f2e5465787400196f72672e6170616368652e6861646f6f702e696f2e546578740141

太浪费空间了,而且类型一般是已知的,也就那么几个,那么它的代替方法出现,看下一小节

GenericWritable类型

使用GenericWritable时,只需继承于他,并通过重写getTypes方法指定哪些类型需要支持即可,我们看下用法:
[java] view plaincopy
  1. package com.sweetop.styhadoop;
  2. import org.apache.hadoop.io.GenericWritable;
  3. import org.apache.hadoop.io.Text;
  4. import org.apache.hadoop.io.Writable;
  5. class MyWritable extends GenericWritable {
  6. MyWritable(Writable writable) {
  7. set(writable);
  8. }
  9. public static Class<? extends Writable>[] CLASSES=null;
  10. static {
  11. CLASSES=  (Class<? extends Writable>[])new Class[]{
  12. Text.class
  13. };
  14. }
  15. @Override
  16. protected Class<? extends Writable>[] getTypes() {
  17. return CLASSES;  //To change body of implemented methods use File | Settings | File Templates.
  18. }
  19. }

然后输出序列化后的结果

[java] view plaincopy
  1. package com.sweetop.styhadoop;
  2. import org.apache.hadoop.io.IntWritable;
  3. import org.apache.hadoop.io.Text;
  4. import org.apache.hadoop.io.VIntWritable;
  5. import org.apache.hadoop.util.StringUtils;
  6. import java.io.IOException;
  7. /**
  8. * Created with IntelliJ IDEA.
  9. * User: lastsweetop
  10. * Date: 13-7-17
  11. * Time: 上午9:51
  12. * To change this template use File | Settings | File Templates.
  13. */
  14. public class TestGenericWritable {
  15. public static void main(String[] args) throws IOException {
  16. Text text=new Text("\u0041\u0071");
  17. MyWritable myWritable=new MyWritable(text);
  18. System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(text)));
  19. System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(myWritable)));
  20. }
  21. }

结果是:

[java] view plaincopy
  1. 024171
  2. 00024171

GenericWritable的序列化只是把类型在type数组里的索引放在了前面,这样就比ObjectWritable节省了很多空间,所以推荐大家使用GenericWritable

集合类型的Writable

ArrayWritable和TwoDArrayWritable

ArrayWritable和TwoDArrayWritable分别表示数组和二维数组的Writable类型,指定数组的类型有两种方法,构造方法里设置,或者继承于ArrayWritable,TwoDArrayWritable也是一样。
[java] view plaincopy
  1. package com.sweetop.styhadoop;
  2. import org.apache.hadoop.io.ArrayWritable;
  3. import org.apache.hadoop.io.Text;
  4. import org.apache.hadoop.io.Writable;
  5. import org.apache.hadoop.util.StringUtils;
  6. import java.io.IOException;
  7. /**
  8. * Created with IntelliJ IDEA.
  9. * User: lastsweetop
  10. * Date: 13-7-17
  11. * Time: 上午11:14
  12. * To change this template use File | Settings | File Templates.
  13. */
  14. public class TestArrayWritable {
  15. public static void main(String[] args) throws IOException {
  16. ArrayWritable arrayWritable=new ArrayWritable(Text.class);
  17. arrayWritable.set(new Writable[]{new Text("\u0071"),new Text("\u0041")});
  18. System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(arrayWritable)));
  19. }
  20. }

看下输出:

[java] view plaincopy
  1. 0000000201710141

可知,ArrayWritable以一个整型开始表示数组长度,然后数组里的元素一一排开。

ArrayPrimitiveWritable和上面类似,只是不需要用子类去继承ArrayWritable而已。

MapWritable和SortedMapWritable

MapWritable对应Map,SortedMapWritable对应SortedMap,以4个字节开头,存储集合大小,然后每个元素以一个字节开头存储类型的索引(类似GenericWritable,所以总共的类型总数只能倒127),接着是元素本身,先key后value,这样一对对排开。
这两个Writable以后会用很多,贯穿整个hadoop,这里就不写示例了。
我们注意到没看到set集合和list集合,这个可以代替实现。用MapWritable代替set,SortedMapWritable代替sortedmap,只需将他们的values设置成NullWritable即可,NullWritable不占空间。相同类型构成的list,可以用ArrayWritable代替,不同类型的list可以用GenericWritable实现类型,然后再使用ArrayWritable封装。当然MapWritable一样可以实现list,把key设置为索引,values做list里的元素。

转载于:https://www.cnblogs.com/ihongyan/p/5137275.html

【转】hadoop深入研究:(十一)——序列化与Writable实现相关推荐

  1. hadoop中的序列化与Writable类

    本文地址:http://www.cnblogs.com/archimedes/p/hadoop-writable-class.html,转载请注明源地址. hadoop中自带的org.apache.h ...

  2. hadoop深入研究:(五)——Archives

    转载请注明来源地址:http://blog.csdn.net/lastsweetop/article/details/9123155 简介 我们在hadoop深入研究:(一)--hdfs介绍里已讲过, ...

  3. Hadoop IO 文件压缩 序列化

    文件中使用getcodec()方法,可以将文件属性名映射到一个CompressionCodec,该去文件的Path对象当参数. CodecPool使用原生的代码库并且需要在用执行中执行大量解压和压缩, ...

  4. Hadoop入门(十一)Mapreduce的InputFomrat各种子类

    一.TextInputFormat extends FileInputFomrat<LongWritable,Text>  是默认读取文件的切分器,其内的LineRecordReader: ...

  5. hadoop的Avro数据序列化系统

    1.什么是Avro Avro设计用于支持大批量数据交换的应用 Avro可以将数据结构或者对象转换成便于存储或者传输的格式. 为了hadoop的前途考虑,DougCutting主导开发的一套新的序列化系 ...

  6. hadoop详细笔记(十一) mapreduce数据分析案例之线段重叠案例

    1 数据 1,4 2,5 3,4 2,5 2,4 3,4 2,6 2 需求 统计x轴上每个点线段重叠的次数 3 代码实现 package com._51doit.mr.line;import org. ...

  7. 【大数据之Hadoop】三十一、HDFS集群迁移之Apache和Apache集群间数据拷贝

    数据迁移场景:   冷热集群数据分类存储:集群数据整体迁移:数据的准实时同步,目的在于数据的双备份可用. 数据迁移需要考虑的因素:   带宽.性能.增量同步(原始数据文件进行了追加写.被删除或重命名) ...

  8. java程序员的大数据之路(6):定制的Writable类型

    序列化 序列化是指将结构化对象转化为字节流,以便在网络上传输或写入磁盘.反序列化是指将字节流转回结构化对象的过程. 在Hadoop中,系统中多个节点上进程间的通信是通过"远程过程调用&quo ...

  9. 又双叒叕来更新啦!Hadoop———MapReduce篇

    文章目录 MapReduce(计算) MapReduce概述 MapReduce定义 MapReduce的优缺点 核心思想 MapReduce计算程序运行时的相关进程 官方WordCount源码 Ma ...

最新文章

  1. 日赚 5 亿的腾讯工资又涨了:员工上半年人均月薪 7.8 万
  2. python【力扣LeetCode算法题库】169-多数元素
  3. opencv三维重建_基于OpenCV和C++的多视图三维重建
  4. CTF-Python打包成的exe文件Re逆向
  5. php大量数据库抽象,数据库的数据抽象有几个级别
  6. 从鲁班造木鸢到智能控制,图解世界无人机发展简史
  7. Windows下配置Squid反向代理服务器
  8. 事情又没有后续,吾真想质问头目,汝是为吾干活吗?
  9. Atitit.收银系统pos 以及打印功能的行业标准
  10. Linux系统重要日志文件
  11. 如何快速构建一个企业revit族库(含插件)
  12. c#使用word、excel、pdf ——转
  13. 简单matlab仿真实例教程,simulink仿真教程
  14. 怎么改自己手机的ip地址
  15. pr用什么显卡比较好_用 PR 剪辑视频应该用什么 CPU 和显卡?
  16. Win10提示“为了对电脑进行保护,已经阻止此应用”如何解决
  17. 小白如何装重装操作系统(使用PE辅助)
  18. 安卓集成Google登录并进一步获取性别生日手机号等信息
  19. imx8mqevk OPTEE 全系统构建
  20. 笔记本计算机在桌面显示器,解决笔记本电脑屏幕出现条纹的五大方法

热门文章

  1. java 16进制与汉字_java实现汉字转unicode与汉字转16进制实例
  2. 实战|记一次绕过宝塔防火墙的BC站渗透
  3. 小白都看得懂的监督学习与无监督学习
  4. 隐藏了十年的Sudo漏洞爆出:无需密码就能获取root权限
  5. 解决Nginx添加openssl模块编译时报错问题
  6. C/C++中ASCII与Unicode字符串相互转换
  7. StringBuilder与StringBuffer比较
  8. 输入正方形对角线两个端点坐标,求中点坐标
  9. spark on yarn
  10. 携程基于Quasar协程的NIO实践