各种类型的Writable

2023-05-16

各种类型的Writable(Text、ByteWritable、NullWritable、ObjectWritable、GenericWritable、ArrayWritable、MapWritable、SortedMapWritable)

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类型。
@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);  
} 

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的不同
@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的偏移量,后者是字节的偏移量

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

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

BytesWritable类型

ByteWritable类型是一个二进制数组的封装类型,序列化格式是以一个4字节的整数(这点与Text不同,Text是以变长int开头)开始表明字节数组的长度,然后接下来就是数组本身。看下示例:
@Test  
public void testByteWritableSerilizedFromat() throws IOException {  
    BytesWritable bytesWritable=new BytesWritable(new byte[]{3,5});  
    byte[] bytes=SerializeUtils.serialize(bytesWritable);  
    Assert.assertEquals(StringUtils.byteToHexString(bytes),"000000020305");  
}  

和Text一样,ByteWritable也可以通过set方法修改,getLength返回的大小是真实大小,而getBytes返回的大小确不是。

        bytesWritable.setCapacity(11);  
        bytesWritable.setSize(4);  
        Assert.assertEquals(4,bytesWritable.getLength());  
        Assert.assertEquals(11,bytesWritable.getBytes().length);  

NullWritable类型

NullWritable是一个非常特殊的Writable类型,序列化不包含任何字符,仅仅相当于个占位符。你在使用mapreduce时,key或者value在无需使用时,可以定义为NullWritable。
package com.sweetop.styhadoop;  
  
import org.apache.hadoop.io.NullWritable;  
import org.apache.hadoop.util.StringUtils;  
  
import java.io.IOException;  
  
/** 
 * Created with IntelliJ IDEA. 
 * User: lastsweetop 
 * Date: 13-7-16 
 * Time: 下午9:23 
 * To change this template use File | Settings | File Templates. 
 */  
public class TestNullWritable {  
    public static void main(String[] args) throws IOException {  
        NullWritable nullWritable=NullWritable.get();  
        System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(nullWritable)));  
    }  
}  



ObjectWritable类型

ObjectWritable是其他类型的封装类,包括java原生类型,String,enum,Writable,null等,或者这些类型构成的数组。当你的一个field有多种类型时,ObjectWritable类型的用处就发挥出来了,不过有个不好的地方就是占用的空间太大,即使你存一个字母,因为它需要保存封装前的类型,我们来看下示例:

package com.sweetop.styhadoop;  
  
import org.apache.hadoop.io.ObjectWritable;  
import org.apache.hadoop.io.Text;  
import org.apache.hadoop.util.StringUtils;  
  
import java.io.IOException;  
  
/** 
 * Created with IntelliJ IDEA. 
 * User: lastsweetop 
 * Date: 13-7-17 
 * Time: 上午9:14 
 * To change this template use File | Settings | File Templates. 
 */  
public class TestObjectWritable {  
    public static void main(String[] args) throws IOException {  
        Text text=new Text("\u0041");  
        ObjectWritable objectWritable=new ObjectWritable(text);  
        System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(objectWritable)));  
  
    }  
}  


仅仅是保存一个字母,那么看下它序列化后的结果是什么:
00196f72672e6170616368652e6861646f6f702e696f2e5465787400196f72672e6170616368652e6861646f6f702e696f2e546578740141

太浪费空间了,而且类型一般是已知的,也就那么几个,那么它的代替方法出现,看下一小节

GenericWritable类型

使用GenericWritable时,只需继承于他,并通过重写getTypes方法指定哪些类型需要支持即可,我们看下用法:

package com.sweetop.styhadoop;  
  
import org.apache.hadoop.io.GenericWritable;  
import org.apache.hadoop.io.Text;  
import org.apache.hadoop.io.Writable;  
  
class MyWritable extends GenericWritable {  
  
    MyWritable(Writable writable) {  
        set(writable);  
    }  
  
    public static Class<? extends Writable>[] CLASSES=null;  
  
    static {  
        CLASSES=  (Class<? extends Writable>[])new Class[]{  
                Text.class  
        };  
    }  
  
    @Override  
    protected Class<? extends Writable>[] getTypes() {  
        return CLASSES;  //To change body of implemented methods use File | Settings | File Templates.  
    }  
}  

然后输出序列化后的结果


package com.sweetop.styhadoop;  
  
import org.apache.hadoop.io.IntWritable;  
import org.apache.hadoop.io.Text;  
import org.apache.hadoop.io.VIntWritable;  
import org.apache.hadoop.util.StringUtils;  
  
import java.io.IOException;  
  
/** 
 * Created with IntelliJ IDEA. 
 * User: lastsweetop 
 * Date: 13-7-17 
 * Time: 上午9:51 
 * To change this template use File | Settings | File Templates. 
 */  
public class TestGenericWritable {  
  
    public static void main(String[] args) throws IOException {  
        Text text=new Text("\u0041\u0071");  
        MyWritable myWritable=new MyWritable(text);  
        System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(text)));  
        System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(myWritable)));  
  
    }  
}  

结果是:

024171  
00024171  

GenericWritable的序列化只是把类型在type数组里的索引放在了前面,这样就比ObjectWritable节省了很多空间,所以推荐大家使用GenericWritable

集合类型的Writable

ArrayWritable和TwoDArrayWritable

ArrayWritable和TwoDArrayWritable分别表示数组和二维数组的Writable类型,指定数组的类型有两种方法,构造方法里设置,或者继承于ArrayWritable,TwoDArrayWritable也是一样。
package com.sweetop.styhadoop;  
  
import org.apache.hadoop.io.ArrayWritable;  
import org.apache.hadoop.io.Text;  
import org.apache.hadoop.io.Writable;  
import org.apache.hadoop.util.StringUtils;  
  
import java.io.IOException;  
  
/** 
 * Created with IntelliJ IDEA. 
 * User: lastsweetop 
 * Date: 13-7-17 
 * Time: 上午11:14 
 * To change this template use File | Settings | File Templates. 
 */  
public class TestArrayWritable {  
    public static void main(String[] args) throws IOException {  
        ArrayWritable arrayWritable=new ArrayWritable(Text.class);  
        arrayWritable.set(new Writable[]{new Text("\u0071"),new Text("\u0041")});  
        System.out.println(StringUtils.byteToHexString(SerializeUtils.serialize(arrayWritable)));  
    }  
}  


看下输出:

0000000201710141  


可知,ArrayWritable以一个整型开始表示数组长度,然后数组里的元素一一排开。

ArrayPrimitiveWritable和上面类似,只是不需要用子类去继承ArrayWritable而已。

MapWritable和SortedMapWritable

MapWritable对应Map,SortedMapWritable对应SortedMap,以4个字节开头,存储集合大小,然后每个元素以一个字节开头存储类型的索引(类似GenericWritable,所以总共的类型总数只能倒127),接着是元素本身,先key后value,这样一对对排开。
这两个Writable以后会用很多,贯穿整个hadoop,这里就不写示例了。
 
我们注意到没看到set集合和list集合,这个可以代替实现。用MapWritable代替set,SortedMapWritable代替sortedmap,只需将他们的values设置成NullWritable即可,NullWritable不占空间。相同类型构成的list,可以用ArrayWritable代替,不同类型的list可以用GenericWritable实现类型,然后再使用ArrayWritable封装。当然MapWritable一样可以实现list,把key设置为索引,values做list里的元素。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

各种类型的Writable 的相关文章

随机推荐

  • OpenCV4.7.0、FFmpeg5.1 Nvidia GPU视频硬解码

    1 环境 操作系统 xff1a Ubuntu18 04 GPU xff1a Nvidia GeForce RTX 2080TI 2 安装2080TI驱动 请参考文章 158条消息 NVIDIA GPU 驱动程序安装 洪流之源的博客 CSDN
  • MQ-2烟雾传感器的使用

    一 MQ 2烟雾传感器简介 MQ 2 烟雾传感器采用在清洁空气中电导率较低的二氧化锡 SnO2 xff0c 属于表面离子式N型半导体 当MQ 2烟雾传感器在200到300摄氏度环境时 xff0c 二氧化锡吸附空气中的氧 xff0c 形成氧的
  • HC-SR04超声波测距模块介绍

    超声波简介 超声波是由机械振动产生的 可在不同介质中以不同的速度传播 具有定向性好 能量集中 传输过程中衰减较小 反射能力较强等优点 超声波传感器可广泛应用于非接触式检测方法 它不受光线 被测物颜色等影响 对恶劣的工作环境具有一定的适应能力
  • 液晶12864显示图片

    液晶12864简介 12864是128 64点阵液晶模块的点阵数简称 基本参数 1 低电源电压 xff08 VDD 43 3 0 xff5e 43 5 5V xff09 2 显示分辨率 128 64 点 3 内置汉字字库 xff0c 提供8
  • 液晶12864显示字符

    液晶12864简介 12864是128 64点阵液晶模块的点阵数简称 基本参数 1 低电源电压 xff08 VDD 43 3 0 xff5e 43 5 5V xff09 2 显示分辨率 128 64点 3 内置汉字字库 xff0c 提供81
  • Bash:command:未找到命令

    前言 在Linux系统中 xff0c 经常会遇到这样的问题 xff1a bash xff1a command xff1a 未找到命令 这个真的很烧脑 xff0c 遇到的次数多 xff0c 在网上也查了好多 xff0c 答案五花八门 xff0
  • S32K148----SDK笔记----CAN收发

    文章目录 前言建立工程ProcessorExpert配置发送CAN报文CAN接收中断工程代码微信公众号 前言 S32K148自带3路CAN 官方的SDK给了can pal的例程 本文更基础一点 直接用flexcan组件相关的函数 CANFD
  • TC397开发板KIT_A2G_TC397_5V_TFT简介

    开发板简介 照片资料调试资源供电扩展CAN 用的开发板是KIT A2G TC397 5V TFT 其实更推荐KIT A2G TC397 3V3 TFT 售价 1 670 59 照片 正面 背面 5V的板子和3 3V的板子主要是下面几个器件不
  • AURIX TC397 Flash编程

    目录 Flash编程基础知识Flash Programming微信公众号 Flash编程基础知识 参考 Flash Programming 1 for KIT AURIX TC397 TFT 本例展示了如何烧写PFLASH Program
  • 周立功USBCAN-II的Python调用

    目录 USBCAN II环境配置CAN收发运行zcanpro微信公众号 USBCAN II USBCAN II 或者叫USBCAN2是周立功 致远电子 比较经典的USB接口的CAN卡 有两路标准CAN 最高支持到1M波特率 单用USB就可以
  • 拒绝丧偶式育儿,正确「养育男孩」

    前言 如何养育男孩 xff1f 当写下这几个字的时候 xff0c 我已在心里默念了好几遍 xff0c 不知道该如何回答 在2年前的某个凌晨2点18分 xff0c 第一声婴儿地啼哭 xff0c 护士告诉我 是个男孩 开始 xff0c 我就买了
  • Pandoc 多Markdown转单PDF

    文章目录 Pandoc 简介Pandoc 安装pandoc latex template字体安装Powershell 脚本Ubuntu PandocMarkdown 合并 Pandoc 简介 Pandoc 免费的文档转换器 支持常见的各种文
  • SocketCAN 命名空间 VCAN VXCAN CANGW 举例

    文章目录 NAMESPACESocketCAN最新 can utils 安装VCAN 举例VXCAN 举例CANGW 举例参考 NAMESPACE namespaces 命名空间 将全局系统资源包装在抽象中 使命名空间中的进程看起来拥有自己
  • slcan 协议 脚本 测试

    文章目录 slcan 协议kernel slcancan utils slcanptycan utils slcandcan utils slcan attachpython slcan slcan 协议 slcan 基于文本 ASCII
  • Notepad++直接编译运行Java

    安装Notepad 43 43 和JDK xff08 略 xff09 xff1b Notepad 43 43 的菜单栏 xff1a 插件 gt Plugin Manager gt Show Plugin Manager xff0c Avai
  • 几款自带编码器的直流电机

    不少小伙伴做两轮自平衡车或者机器人或者各种比赛时 xff0c 经常需要用到直流电机带编码器 xff0c 有的自己可以做外置的编码器 xff0c 但是否有自带编码器或码盘的直流电机 xff1f 答案是肯定的 xff0c 这里就推荐几款带编码器
  • 四大编辑器 -- Sublime, Atom, VS Code和Notepad++

    主要介绍3个跨平台的 Sublime Atom VS Code 一个Windows的 Notepad 43 43 Sublime Text 官网 https www sublimetext com Sublime Text is a sop
  • AD绘制四层板

    原理图导入PCB后 默认是双层板 可以Design gt Layer Stack Manager 点击Top Layer 然后点击Add Plane 负片层 电脑上在此层走线表示把铜挖空 这里把一整层都当作GND 非GND的过孔打到这一层
  • 群晖(Synology)配置 NAS + 软路由

    文章目录 背景结论软路由配置 背景 老板上周兴致勃勃的找到我 说Macbook存储不够用了 帮他找个存东西的 不是移动硬盘 我也很懵逼 我搞活动捡便宜充的百度网盘超级会员 觉得自己很是尾巴翘上天的土豪了 但想到老板的身价 又不太清楚macb
  • 各种类型的Writable

    各种类型的Writable xff08 Text ByteWritable NullWritable ObjectWritable GenericWritable ArrayWritable MapWritable SortedMapWri