hadoop中的序列化与Writable类

hadoop中自带的org.apache.hadoop.io包中有广泛的writable类可供选择,它们形成下图所示的层次结构:

java基本类型的Writable封装器

Writable类对java基本类型提供封装,short和char除外,所有的封装包含get()和set()两个方法用于读取或设置封装的值

java基本类型的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类型。

public void testTextIndex(){
        Text text=new Text("hadoop");
        Assert.assertEquals(text.getLength(), 6);
        Assert.assertEquals(text.getBytes().length, 6);
        Assert.assertEquals(text.charAt(2),(int)'d');
        Assert.assertEquals("Out of bounds",text.charAt(100),-1);
}

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

public void testTextFind() {
        Text text = new Text("hadoop");
        Assert.assertEquals("find a substring",text.find("do"),2);
        Assert.assertEquals("Find first 'o'",text.find("o"),3);
        Assert.assertEquals("Find 'o' from position 4 or later",text.find("o",4),4);
        Assert.assertEquals("No match",text.find("pig"),-1);
}

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的不同

import java.io.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.util.StringUtils;
import junit.framework.Assert;
public class textandstring {
        public static void string() throws UnsupportedEncodingException {
            String str = "\u0041\u00DF\u6771\uD801\uDC00";
            Assert.assertEquals(str.length(), 5);
            Assert.assertEquals(str.getBytes("UTF-8").length, 10);

            Assert.assertEquals(str.indexOf("\u0041"), 0);
            Assert.assertEquals(str.indexOf("\u00DF"), 1);
            Assert.assertEquals(str.indexOf("\u6771"), 2);
            Assert.assertEquals(str.indexOf("\uD801\uDC00"), 3);

            Assert.assertEquals(str.charAt(0), '\u0041');
            Assert.assertEquals(str.charAt(1), '\u00DF');
            Assert.assertEquals(str.charAt(2), '\u6771');
            Assert.assertEquals(str.charAt(3), '\uD801');
            Assert.assertEquals(str.charAt(4), '\uDC00');

            Assert.assertEquals(str.codePointAt(0), 0x0041);
            Assert.assertEquals(str.codePointAt(1), 0x00DF);
            Assert.assertEquals(str.codePointAt(2), 0x6771);
            Assert.assertEquals(str.codePointAt(3), 0x10400);
        }

        public static void text() {
            Text text = new Text("\u0041\u00DF\u6771\uD801\uDC00");
            Assert.assertEquals(text.getLength(), 10);

            Assert.assertEquals(text.find("\u0041"), 0);
            Assert.assertEquals(text.find("\u00DF"), 1);
            Assert.assertEquals(text.find("\u6771"), 3);
            Assert.assertEquals(text.find("\uD801\uDC00"), 6);

            Assert.assertEquals(text.charAt(0), 0x0041);
            Assert.assertEquals(text.charAt(1), 0x00DF);
            Assert.assertEquals(text.charAt(3), 0x6771);
            Assert.assertEquals(text.charAt(6), 0x10400);
        }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        text();
        try {
            string();
        } catch(UnsupportedEncodingException ex) {

        }
    }
}

这样一比较就很明显了。

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对象转换为java.nio.ByteBuffer对象,然后再利用缓冲区对Text对象反复调用bytesToCodePoint方法,该方法
能获取下一代码的位置,并返回相应的int值,最后更新缓冲区中的位置。通过bytesToCodePoint()方法可以检测出字符串的末尾,并返回
-1值。看一下示例代码:

import java.io.*;
import java.nio.ByteBuffer;
import org.apache.hadoop.io.*;
import org.apache.hadoop.util.StringUtils;
import junit.framework.Assert;
public class textandstring {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Text t = new Text("\u0041\u00DF\u6771\uD801\uDC00");
        ByteBuffer buf = ByteBuffer.wrap(t.getBytes(), 0, t.getLength());
        int cp;
        while(buf.hasRemaining() && (cp = Text.bytesToCodePoint(buf)) != -1) {
            System.out.println(Integer.toHexString(cp));
        }
    }
}

运行结果:

41
df
6771
10400

Text的修改

除了NullWritable是不可更改外,其他类型的Writable都是可以修改的。你可以通过Text的set方法去修改去修改重用这个实例。

public void testTextMutability() {
    Text text = new Text("hadoop");
    text.set("pig");
    Assert.assertEquals(text.getLength(), 3);
    Assert.assertEquals(text.getBytes().length, 3);
}  

注意:在某些情况下,getBytes()方法返回的字节数组可能比getLength()函数返回的长度更长:

import java.io.*;
import java.nio.ByteBuffer;
import org.apache.hadoop.io.*;
import org.apache.hadoop.util.StringUtils;
import junit.framework.Assert;
public class textandstring {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Text t = new Text("hadoop");
        t.set(new Text("pig"));
        Assert.assertEquals(t.getLength(), 3);
        Assert.assertEquals(t.getBytes().length, 6);
    }
}

Text类并不像String类那样有丰富的字符串操作API,所以多数情况下,需要将Text对象转换成String对象。这一转换过程通过调用ToString()方法来实现

时间: 2024-08-30 05:20:07

hadoop中的序列化与Writable类的相关文章

hadoop中的序列化与Writable接口

简介 序列化和反序列化就是结构化对象和字节流之间的转换,主要用在内部进程的通讯和持久化存储方面. 通讯格式需求 hadoop在节点间的内部通讯使用的是RPC,RPC协议把消息翻译成二进制字节流发送到远程节点,远程节点再通过反序列化把二进制流转成原始的信息.RPC的序列化需要实现以下几点: 1.压缩,可以起到压缩的效果,占用的宽带资源要小. 2.快速,内部进程为分布式系统构建了高速链路,因此在序列化和反序列化间必须是快速的,不能让传输速度成为瓶颈. 3.可扩展的,新的服务端为新的客户端增加了一个参

一脸懵逼学习Hadoop中的序列化机制——流量求和统计MapReduce的程序开发案例——流量求和统计排序

一:序列化概念 序列化(Serialization)是指把结构化对象转化为字节流.反序列化(Deserialization)是序列化的逆过程.即把字节流转回结构化对象.Java序列化(java.io.Serializable) 二:Hadoop序列化的特点 (1):序列化格式特点: 紧凑:高效使用存储空间. 快速:读写数据的额外开销小. 可扩展:可透明地读取老格式的数据. 互操作:支持多语言的交互. (2):Hadoop的序列化格式:Writable接口 三:Hadoop序列化的作用: (1):

hadoop中实现定制Writable类

Hadoop中有一套Writable实现可以满足大部分需求,但是在有些情况下,我们需要根据自己的需要构造一个新的实现,有了定制的Writable,我们就可以完全控制二进制表示和排序顺序. 为了演示如何新建一个定制的writable类型,我们需要写一个表示一对字符串的实现: blic class TextPair implements WritableComparable<TextPair> { private Text first; private Text second; public Te

hadoop中典型Writable类详解

Hadoop将很多Writable类归入org.apache.hadoop.io包中,在这些类中,比较重要的有Java基本类.Text.Writable集合.ObjectWritable等,重点介绍Java基本类和ObjectWritable的实现. 1. Java基本类型的Writable封装 目前Java基本类型对应的Writable封装如下表所示.所有这些Writable类都继承自WritableComparable.也就是说,它们是可比较的.同时,它们都有get()和set()方法,用于

hadoop详解(十一) 序列化与Writable实现

简介 在hadoop中,Writable的实现类是个庞大的家族,我们在这里简单的介绍一下常用来做序列化的一部分. java原生类型 除char类型以外,所有的原生类型都有对应的Writable类,并且通过get和set方法可以他们的值. IntWritable和LongWritable还有对应的变长VIntWritable和VLongWritable类. 固定长度还是变长的选用类似与数据库中的char或者vchar,在这里就不赘述了. Text类型 Text类型使用变长int型存储长度,所以Te

hadoop详解(十) 序列化与Writable接口

简介 序列化和反序列化就是结构化对象和字节流之间的转换,主要用在内部进程的通讯和持久化存储方 面. 通讯格式需求 hadoop在节点间的内部通讯使用的是RPC,RPC协议把消息翻译成二进制字节流发 送到远程节点,远程节点再通过反序列化把二进制流转成原始的信息.RPC的序列化需要实现以下几点: 1. 压缩,可以起到压缩的效果,占用的宽带资源要小. 2.快速,内部进程为分布式系统构建了高速链路,因 此在序列化和反序列化间必须是快速的,不能让传输速度成为瓶颈. 3.可扩展的,新的服务端为新的客户 端增

自定义Hadoop的可序列化类

java原生语言中要想一个自定义类可序列化,很简单,只要让这个类实现java.io.Serializable接口就可以了,但是在Hadoop框架中,要想让自定义类可以被序列化,我们必须手动让其实现WritableCompable接口并且实现write(),readFields(),compareTo()方法. 下面就是一个我们自定义的可序列化的类: /* */ package com.charles.writable; import java.io.DataInput; import java.

Hadoop序列化与Writable接口(二)

上一篇文章Hadoop序列化与Writable接口(一)介绍了Hadoop序列化,Hadoop Writable接口以及如何定制自己的Writable类,在本文中我们继续Hadoop Writable类的介绍,这一次我们关注的是Writable实例序列化之后占用的字节长度,以及Writable实例序列化之后的字节序列的构成. 为什么要考虑Writable类的字节长度 大数据程序还需要考虑序列化对象占用磁盘空间的大小吗?也许你会认为大数据不是就是数据量很大吗,那磁盘空间一定是足够足够的大,一个序列

Hadoop中使用FileStatus类来查看HDFS中文件或目录的元信息

Hadoop中的FileStatus类可以用来查看HDFS中文件或者目录的元信息,任意的文件或者目录都可以拿到对应的FileStatus, 我们这里简单的演示下这个类的相关API: /* */ package com.charles.hadoop.fs; import java.net.URI; import java.sql.Timestamp; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.F