这篇来说说非数值类型.
Text
Hadoop中没有StringWritable或CharWritable,取而代之的是Text类型,Text采用UTF-8编码.
Text怎么序列化?对于英文和符号来说比较简单,直接用ASCII码就可以了:
String hadoop = "hadoop";
String java = "Java";
String symbols = "!@#$%^&*()";
Utils.printArray(Utils.serialize(new Text(hadoop)));
Utils.printArray(Utils.serialize(new Text(java)));
Utils.printArray(Utils.serialize(new Text(symbols)));
结果:
[6, 104, 97, 100, 111, 111, 112]
[4, 74, 97, 118, 97]
[10, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41]
第一位数字是有几个字符(也就是数组除它之外有几个元素),后面就是按ASCII提取的数字.那么汉字呢?
String[] strings = {"举", "头望", "明月"};
for (String string : strings) {
Utils.printArray(Utils.serialize(new Text(string)));
}
结果:
[3, -28, -72, -66]
[6, -27, -92, -76, -26, -100, -101]
[6, -26, -104, -114, -26, -100, -120]
一个字第一位就是3,两个字就是6,可以想到,一个汉字对应3个元素.,与UTF-8编码中汉字的字节数对应.
下面把Text跟String对比一下,看看两者有什么不同:先来看看两者都有的charAt(),输出结果以注释的形式写在输出语句后面:
String java = "Java";
System.out.println(java.charAt(1)); //a
Text text = new Text(java);
System.out.println(text.charAt(0)); //74
System.out.println(text.charAt(1)); //97
System.out.println(text.charAt(2)); //118
System.out.println(text.charAt(3)); //97
String.charAt()的返回值为char,Text的为int类型.String的方法很好理解,就是输出对应下标的字符,那么Text呢,对于ASCII字符,就是序列化后的字节数组的对应下标的元素,汉字的话,来看看下面的代码:
Text poem = new Text("静夜思一二三四五六七");
System.out.println(poem.getLength());
Utils.printArray(Utils.serialize(poem));
System.out.print("[");
for (int i = 0; i < poem.getLength(); i++) {
System.out.print(poem.charAt(i) + " , ");
}
System.out.println("]");
运行结果:
30
[30, -23, -99, -103, -27, -92, -100, -26, -128, -99, -28, -72, -128, -28, -70, -116, -28,
-72, -119, -27, -101, -101, -28, -70, -108, -27, -123, -83, -28, -72, -125]
[38745 , -1 , -1 , 22812 , -1 , -1 , 24605 , -1 , -1 , 19968 , -1 , -1 , 20108 , -1 , -1 ,
19977 , -1 , -1 , 22235 , -1 , -1 , 20116 , -1 , -1 , 20845 , -1 , -1 , 19971 , -1 , -1 , ]
第一组结果是getLength()的结果,第二组结果是输出序列化后的字节数组,第三组结果是直接利用text的charAt()输出.
Text的getLength()是获取序列化后的字节数组的元素数,前面我们知道序列化后的数组第一个元素等于这个数组包含的元素数,所以也可以说这个方法输出的是序列化后的数组的第一个元素.
按前面对ASCII的分析,第二组结果与第三组结果应该是一样的,但是实际情况是第三组结果的结构是:[Unicode码, -1, -1],所以还是应该注意区分.
Text不像String有那么多方法进行各种操作,所以一般都是先转换为String进行操作再转换为Text,具体来说就是对Text对象调用toString().
其他Writable
ByteWritable
是对二进制数据数组的封装.它的序列化格式为一个置顶所含字节数的整数域(4字节),后跟数据内容本身:
BytesWritable b1 = new BytesWritable(new byte[]{ 3, 5 });
BytesWritable b2 = new BytesWritable(new byte[]{ 10, 50, 60, 1 });
byte[] bytes1 = Utils.serialize(b1);
byte[] bytes2 = Utils.serialize(b2);
Utils.printArray(bytes1); //000000020305
Utils.printArray(bytes2); //000000040a323c01
数组中的数字被转换为了16进制.
NullWritable
是一种特殊的Writable,它的序列化长度为0.它不从数据流中读写数据,只充当占位符.例如在MapReduce中,如果不需要具体的键或值,就可以使用NullWritable,这样就不需要使用与要处理的数据不相关的对象占用存储空间和网络带宽了.如果想要获取这个实例,需要使用静态方法NullWritable.get().