Hadoop入门——序列化与Writable接口

一.序列化概念

序列化(Serialization)是指把结构化对象转化为字节流。
反序列化(Deserialization)是序列化的逆过程。即把字节流转回结构化对象。
Java序列化(java.io.Serializable)

 

二.Hadoop序列化的特点

1.紧凑:高效使用存储空间;
2.快速:读写数据的额外开销小;
3.可扩展:可透明地读取老格式的数据;
4.互操作:支持多语言的交互;

 

三.Hadoop序列化作用

序列化在分布式环境的两大作用:进程间通信,永久存储。

 

四.Writable接口

Writable接口, 是根据 DataInput 和 DataOutput 实现的简单、有效的序列化对象。

MapReduce的任意Key和Value必须实现Writable接口!

MapReduce的任意key必须实现WritableComparable接口(多用于Java值对象的比较,也可分别实现Writable和Comparable接口)!

 

为何不适用java序列化?

1.java序列化不够灵活,为了更好的控制序列化的整个流程所以使用Writable

2.java序列化不符合序列化的标准,没有做一定的压缩,java序列化首先写类名,然后再是整个类的数据,而且成员对象在序列化中只存引用,成员对象的可以出现的位置很随机,既可以在序列化的对象前,也可以在其后面,这样就对随机访问造成影响,一旦出错,整个后面的序列化就会全部错误,但是
Writable完美的弥补了这一点,因为Writable中每一条纪录间是相互独立的
3.Java序列化每次序列化都要重新创建对象,内存消耗大,而Writable是可以重用的。

 

五.关于类型

1.java原生类型

除char类型以外,所有的原生类型都有对应的Writable类,并且通过get和set方法可以他们的值。

IntWritable和LongWritable还有对应的变长VIntWritable和VLongWritable类。

固定长度还是变长的选用类似与数据库中的char或者vchar,在这里就不赘述了。

 

2.Text类型

Text类型使用变长int型存储长度,所以Text类型的最大存储为2G.

Text类型采用标准的utf-8编码,所以与其他文本工具可以非常好的交互,但要注意的是,这样的话就和java的String类型差别就很多了。

3.text和String的区别

a.检索的不同

Text的chatAt返回的是一个整型,及utf-8编码后的数字,而不是象String那样的unicode编码的char类型。

    @Test
    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方法

    @Test
    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);
    }

b.unicode不同

当uft-8编码后的字节大于两个时,Text和String的区别就会更清晰,因为String是按照unicode的char计算,而Text是按照字节计算。

我们来看下1到4个字节的不同的unicode字符

Hadoop入门——序列化与Writable接口_第1张图片

4个unicode分别占用1到4个字节,u+10400在java的unicode字符重占用两个char,前三个字符分别占用1个char

我们通过代码来看下String和Text的不同

 @Test
    public 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);
    }
 
    @Test
    public 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);
    }

这样一比较就很明显了。

1.String的length()方法返回的是char的数量,Text的getLength()方法返回的是字节的数量。

2.String的indexOf()方法返回的是以char为单元的偏移量,Text的find()方法返回的是以字节为单位的偏移量。

3.String的charAt()方法不是返回的整个unicode字符,而是返回的是java中的char字符

4.String的codePointAt()和Text的charAt方法比较类似,不过要注意,前者是按char的偏移量,后者是字节的偏移量

 

c.text的迭代

在Text中对unicode字符的迭代是相当复杂的,因为与unicode所占的字节数有关,不能简单的使用index的增长来确定。首先要把Text对象使用ByteBuffer进行封装,然后再调用Text的静态方法bytesToCodePoint对ByteBuffer进行轮询返回unicode字符的code point。看一下示例代码:

package com.sweetop.styhadoop;
 
import org.apache.hadoop.io.Text;
 
import java.nio.ByteBuffer;
 
/**
 * Created with IntelliJ IDEA.
 * User: lastsweetop
 * Date: 13-7-9
 * Time: 下午5:00
 * To change this template use File | Settings | File Templates.
 */
public class TextIterator {
    public static void main(String[] args) {
        Text text = new Text("\u0041\u00DF\u6771\uD801\udc00");
        ByteBuffer buffer = ByteBuffer.wrap(text.getBytes(), 0, text.getLength());
        int cp;
        while (buffer.hasRemaining() && (cp = Text.bytesToCodePoint(buffer)) != -1) {
            System.out.println(Integer.toHexString(cp));
        }
    }
}

d.text的修改

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

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

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

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

 

 

 

 

你可能感兴趣的:(Hadoop,Hadoop入门教程)