Hadoop(10) 序列化--Writable 类

本文详细介绍了 Hadoop 中的 Writable 接口及其实现类,包括基本类型的 Writable 对象、Text 类型的特点及其与其他 Java 字符串类型的差异、BytesWritable 的序列化过程等。此外还探讨了 ArrayWritable 和 MapWritable 等集合类型的使用方法。

java基本类型的Writable

      除char类型以外,所有的java类型都有对应的Writable类,并且通过get()和set()读取或存储。

java基本类型Writable序列化大小(字节)
booleanBooleanWritable1
byteByteWritable1
shortShortWritable2
intIntWritable4
intVIntWritable1~5
floatFloatWritable4
longLongWritable8
longVLongWritable1~9
doubleDoubleWritable8



      对整数进行编码,可选择长格式(IntWritable、LongWritable)和变长格式(VIntWritable、VLongWritable)。定长格式适合对整个值域空间中分布非常均匀的数值进行编码;但大多数数值变量分布不均匀,而且变长格式一般更节省空间。变长格式可以在VIntWritable、VLongWritable中转换。

Text类型

      Text是针对UTF-8序列的Writable类。Text类使用整形变长方式存储,Text类型的最大存储为2G.
      Text类型采用utf-8编码,这样就和java的String类型之间存在一定差别。

  • 索引   Text类的索引是根据编码字节序列中的位置实现的,并非字符串中的Unicode字符;Text的charAt() 返回值是一个表示Unicode编码位置的int类型值(即十进制表示的ASCII码),并非java.lang.String 中返回一个char类型值
@Test
public void index() {
    Text t = new Text("hadoop");
    System.out.println(t.getLength());       // 6
    System.out.println(t.getBytes().length); // 6
    System.out.println(t.charAt(1));         // 97
    System.out.println(t.charAt(100));       // -1
    // 可通过toString()之后再charAt()    
    System.out.println(t.toString().charAt(2)); // 'd'
    }
/**Text中的find()与java.lang.String中的indexOf()类似*/
@Test
public void find() {
    Text t = new Text("hadoop");
    System.out.println(t.find("o"));     // 3
    System.out.println(t.find("do"));    // 2
    System.out.println(t.find("do", 1)); // 2
    System.out.println(t.find("do", 3)); // -1

    System.out.println(t.toString().indexOf("o")); // 3
}
  • Unicode
    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中的Unicode字符进行迭代是非常复杂的,因为与unicode所占的字节数有关,不能简单的使用index的增长来确定。首先得把Text对象使用java.nio.ByteBuffer进行封装,然后再调用Text的静态方法bytesToCodePoint对ByteBuffer进行轮询返回unicode字符的代码位置,最后更新缓冲区的位置。当bytesToCodePoint()返回-1时,即到字符串末尾。

@Test
public void iterator() {
    Text text = new Text("\u0041\u00DF\u6771\uD801\udc00");
    ByteBuffer buf = ByteBuffer.wrap(text.getBytes(), 0, text.getLength());
    int codePoint;
    int EOF = -1;
    while (buf.hasRemaining()
            && EOF != (codePoint = Text.bytesToCodePoint(buf))) {
        System.out.println(Integer.toHexString(codePoint));
    }
}
// 依次打印字符串中四个字符的编码点
// 41 df 6771 10400
  • 可变性   除了NullWritable是不可更改外,其他类型的Writable都是可以修改的。你可以通过Text的set方法去修改去修改重用这个实例
@Test
public void variable() {
    Text t = new Text("hadoop");
    t.set("Hbase");
    System.out.println(t.toString());  // Hbase
    System.out.println(t.getLength()); // 5
    System.out.println(t.getBytes().length); // 5
}
/*注:在调用getBytes()方法之前最好调用一下getLength()方法,由此知道字节数组里有多少有效的字符*/
  • BytesWritable   ByteWritable类型是一个二进制数组的封装类型,序列化格式是以一个4字节的整数(这点与Text不同,Text是以变长int开头)开始表明字节数组的长度,然后接下来就是数组本身。BytesWritable是可变的,其值可通过set()进行修改。
@Test
public void bytesWritableSerilized() throws IOException {
    BytesWritable bytesWritable = new BytesWritable(new byte[] { 3, 5 });
    byte[] bytes = serialize(bytesWritable);
    System.out.println(StringUtils.byteToHexString(bytes)); // 000000020305
}

/**
 * 将一个实现了Writable接口的对象序列化成字节流
 * 
 * @param writable
 * @return
 * @throws java.io.IOException
 */
public static byte[] serialize(Writable writable) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    DataOutputStream dataOut = new DataOutputStream(out);
    writable.write(dataOut);
    dataOut.close();
    return out.toByteArray();
}
  • NullWritable   NullWritable是一个特殊的Writable类型,序列化长度为0。既不从数据流中读数据,也不写数据。仅充占位符。你在使用mapreduce时,key或value在无需使用时,可以定义为NullWritable。

  • ObjectWritable   对java基本类型(String、enum、Writable、null或这些类型构成的数组)的一个通用封装。在Hadoop RPC 中对于方法的参数和返回类型进行封装和解封装。当你的一个field有多种类型时,ObjectWritable类型的用处就发挥出来了,不过有个不好的地方就是占用的空间太大,即使是存一个字母。因为它需要保存封装前的类型。

  • Writable集合

    • ArrayWritable和TwoDArrayWritable分别表示数组和二维数组的Writable类型,指定数组的类型有两种方法:构造方法里设置,或继承于ArrayWritable,TwoDArrayWritable。

    • MapWritable对应Map,SortedMapWritable对应SortedMap。每个键/值字段使用的类型是相应字段序列化形式的一部分。以4个字节开头,存储集合大小,然后每个元素以一个字节开头存储类型的索引(类似GenericWritable,所以总共的类型总数只能倒127),接着是元素本身,先key后value,这样一对对排开。

// ArrayWritable
@Test
public void arrayWritable() throws IOException {
    ArrayWritable arrayWritable = new ArrayWritable(Text.class);
    arrayWritable.set(new Writable[] { new Text("aaa"), new Text("bbb") });

    Writable[] arr = arrayWritable.get();
    for (Writable writable : arr) {
        System.out.println(writable.toString());
    }
    // 转为 16 进制字符串
    System.out
            .println(StringUtils.byteToHexString(serialize(arrayWritable)));

    Object obj = arrayWritable.toArray();
    Text[] arr2 = (Text[]) obj;
    for (Text text : arr2) {
        System.out.println(text);
    }
}
// MapWritable
@Test
public void mapWritable() throws IOException {
    MapWritable map = new MapWritable();
    map.put(new IntWritable(1), new Text("aaa"));
    map.put(new VIntWritable(2), new VLongWritable(1111111111));

    MapWritable dest = new MapWritable();
    WritableUtils.cloneInto(dest, map);
    System.out.println(dest.get(new IntWritable(1)));

    for (Entry<Writable, Writable> entry : map.entrySet()) {
        Writable key = entry.getKey();
        Writable val = entry.getValue();
        System.out.println("key = " + key + " val = " + val);
    }

    Iterator<Entry<Writable, Writable>> it = dest.entrySet().iterator();
    while (it.hasNext()) {
        Entry<Writable, Writable> entry = it.next();
        Writable key = entry.getKey();
        Writable val = entry.getValue();
        System.out.println("key = " + key + " val = " + val);
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值