java基本类型的Writable
除char类型以外,所有的java类型都有对应的Writable类,并且通过get()和set()读取或存储。
| java基本类型 | Writable | 序列化大小(字节) |
|---|---|---|
| boolean | BooleanWritable | 1 |
| byte | ByteWritable | 1 |
| short | ShortWritable | 2 |
| int | IntWritable | 4 |
| int | VIntWritable | 1~5 |
| float | FloatWritable | 4 |
| long | LongWritable | 8 |
| long | VLongWritable | 1~9 |
| double | DoubleWritable | 8 |
对整数进行编码,可选择长格式(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);
}
}
本文详细介绍了 Hadoop 中的 Writable 接口及其实现类,包括基本类型的 Writable 对象、Text 类型的特点及其与其他 Java 字符串类型的差异、BytesWritable 的序列化过程等。此外还探讨了 ArrayWritable 和 MapWritable 等集合类型的使用方法。
1万+

被折叠的 条评论
为什么被折叠?



