Hadoop 典型Writable类详解

2023-05-16

Hadoop 典型Writable类详解

Hadoop将很多Writable类归入org.apache.hadoop.io包中,在这些类中,比较重要的有Java基本类、TextWritable集合、ObjectWritable等,重点介绍Java基本类和ObjectWritable的实现。

1. Java基本类型的Writable封装

目前Java基本类型对应的Writable封装如下表所示。所有这些Writable类都继承自WritableComparable。也就是说,它们是可比较的。同时,它们都有get()set()方法,用于获得和设置封装的值。

Java基本类型对应的Writable封装

Java基本类型Writable序列化后长度
布尔型(boolean)BooleanWritable1
字节型(byte)ByteWritable1
整型(int)IntWritable
VIntWritable
4
1~5
浮点型(float)FloatWritable4
长整型(long)LongWritable
VLongWritable
8
1~9
双精度浮点型(double)DoubleWritable8

在表中,对整型(int和long)进行编码的时候,有固定长度格式(IntWritableLongWritable)和可变长度格式(VIntWritableVLongWritable)两种选择。固定长度格式的整型,序列化后的数据是定长的,而可变长度格式则使用一种比较灵活的编码方式,对于数值比较小的整型,它们往往比较节省空间。同时,由于VIntWritableVLongWritable的编码规则是一样的,所以VIntWritable的输出可以用VLongWritable读入。下面以VIntWritable为例,说明Writable的Java基本类封装实现。代码如下:

public class VIntWritable implements WritableComparable {
    private int value;
    // ……
    // 设置VIntWritable的值
    public void set(int value) { this.value = value; }
	// 获取VIntWritable的值
	public int get() { return value; }
		
	public void readFields(DataInput in) throws IOException {
		value = WritableUtils.readVInt(in);
	}
		
	public void write(DataOutput out) throws IOException {
		WritableUtils.writeVInt(out, value);
	}

}

首先,每个Java基本类型的Writable封装,其类的内部都包含一个对应基本类型的成员变量valueget()set()方法就是用来对该变量进行取值/赋值操作的。而Writable接口要求的readFields()write()方法,VIntWritable则是通过调用Writable工具类中提供的readVInt()writeVInt()读/写数据。方法readVInt()writeVInt()的实现也只是简单调用了readVLong()writeVLong(),所以,通过writeVInt()写的数据自然可以通过readVLong()读入。

writeVLong ()方法实现了对整型数值的变长编码,它的编码规则如下:

如果输入的整数大于或等于–112同时小于或等于127,那么编码需要1字节;否则,序列化结果的第一个字节,保存了输入整数的符号和后续编码的字节数。符号和后续字节数依据下面的编码规则(又一个规则):

如果是正数,则编码值范围落在–113和–120间(闭区间),后续字节数可以通过–(v+112)计算。

如果是负数,则编码值范围落在–121和–128间(闭区间),后续字节数可以通过–(v+120)计算。

后续编码将高位在前,写入输入的整数(除去前面全0字节)。代码如下:

public final class WritableUtils  {
   public stati cvoid writeVInt(DataOutput stream, int i) throws IOException
   {
      writeVLong(stream, i);
   }
   /**
    * @param stream保存系列化结果输出流
    * @param i 被序列化的整数
    * @throws java.io.IOException
    */
   public static void writeVLong(DataOutput stream, long i) throws……
   {
      //处于[-112, 127]的整数
      if (i >= -112 && i <= 127) {
         stream.writeByte((byte)i);
         return;
      }
      //计算情况2的第一个字节
      int len = -112;
      if (i < 0) {
         i ^= -1L;
         len = -120;
      }
      long tmp = i;
      while (tmp != 0) {
         tmp = tmp >> 8;
         len--;
      }
      stream.writeByte((byte)len);
      len = (len < -120) ? -(len + 120) : -(len + 112);
      //输出后续字节
      for (int idx = len; idx != 0; idx--) {
         int shiftbits = (idx - 1) * 8;
         long mask = 0xFFL << shiftbits;
         stream.writeByte((byte)((i & mask) >> shiftbits));
      }
   }
}

2. ObjectWritable类的实现

针对Java基本类型、字符串、枚举、Writable、空值、Writable的其他子类,ObjectWritable提供了一个封装,适用于字段需要使用多种类型。ObjectWritable可应用于Hadoop远程过程调用中参数的序列化和反序列化;ObjectWritable的另一个典型应用是在需要序列化不同类型的对象到某一个字段,如在一个SequenceFile的值中保存不同类型的对象(如LongWritable值或Text值)时,可以将该值声明为ObjectWritable

ObjectWritable的实现比较冗长,需要根据可能被封装在ObjectWritable中的各种对象进行不同的处理。ObjectWritable有三个成员变量,包括被封装的对象实例instance、该对象运行时类的Class对象和Configuration对象。

ObjectWritablewrite方法调用的是静态方法ObjectWritable.writeObject(),该方法可以往DataOutput接口中写入各种Java对象。

writeObject()方法先输出对象的类名(通过对象对应的Class 对象的getName()方法获得),然后根据传入对象的类型,分情况序列化对象到输出流中,也就是说,对象通过该方法输出对象的类名,对象序列化结果对到输出流中。在ObjectWritable.writeObject()的逻辑中,需要分别处理nullJava数组、字符串StringJava基本类型、枚举和Writable的子类6种情况,由于类的继承,处理Writable时,序列化的结果包含对象类名,对象实际类名和对象序列化结果三部分。

为什么需要对象实际类名呢?根据Java的单根继承规则,ObjectWritable中传入的declaredClass,可以是传入instance对象对应的类的类对象,也可以是instance对象的父类的类对象。但是,在序列化和反序列化的时候,往往不能使用父类的序列化方法(如write方法)来序列化子类对象,所以,在序列化结果中必须记住对象实际类名。相关代码如下:

public class ObjectWritable implements Writable, Configurable {
   private Class declaredClass;//保存于ObjectWritable的对象对应的类对象
   private Object instance;//被保留的对象
   private Configuration conf;

   public ObjectWritable() {}

   public ObjectWritable(Object instance) {
      set(instance);
   }

   public ObjectWritable(Class declaredClass, Object instance) {
      this.declaredClass = declaredClass;
      this.instance = instance;
   }           
   ……
   public void readFields(DataInput in) throws IOException {
      readObject(in, this, this.conf);
   }

   public void write(DataOutput out) throws IOException {
      writeObject(out, instance, declaredClass, conf);
   }
   ……
   public static void writeObject(DataOutput out, Object instance,
         Class declaredClass,Configuration conf) throws……{

      if (instance == null) {//空
         instance = new NullInstance(declaredClass, conf);
         declaredClass = Writable.class;
      }

      // 写出declaredClass的规范名
      UTF8.writeString(out, declaredClass.getName());

      if (declaredClass.isArray()) {//数组
         ……
      } else if (declaredClass == String.class) {//字符串
         ……
      } else if (declaredClass.isPrimitive()) {//基本类型
         if (declaredClass == Boolean.TYPE) {        //boolean
            out.writeBoolean(((Boolean)instance).booleanValue());
         } else if (declaredClass == Character.TYPE) { //char
            ……
         }
      } else if (declaredClass.isEnum()) {//枚举类型
         ……
      } else if (Writable.class.isAssignableFrom(declaredClass)) {
         //Writable的子类
         UTF8.writeString(out, instance.getClass().getName());
         ((Writable)instance).write(out);
      } else {
         ……
   }

   public static Object readObject(DataInput in,
                  ObjectWritable objectWritable, Configuration conf){
      ……
      Class instanceClass = null;
      ……
      Writable writable = WritableFactories.newInstance(instanceClass,
            conf);
      writable.readFields(in);
      instance = writable;
      ……
   }
}

和输出对应,ObjectWritablereadFields()方法调用的是静态方法ObjectWritable.readObject(),该方法的实现和writeObject()类似,唯一值得研究的是Writable对象处理部分,readObject()方法依赖于WritableFactories类。WritableFactories类允许非公有的Writable子类定义一个对象工厂,由该工厂创建Writable对象,如在上面的readObject()代码中,通过WritableFactories的静态方法newInstance(),可以创建类型为instanceClassWritable子对象。相关代码如下:

public class WritableFactories {
   //保存了类型和WritableFactory工厂的对应关系
   private static final HashMap<Class, WritableFactory>CLASS_TO_FACTORY
      = new HashMap<Class, WritableFactory>();
   ……
   public static Writable newInstance(Class<? extends Writable> c,
                                      Configuration conf) {
      WritableFactory factory = WritableFactories.getFactory(c);
      if (factory != null) {
         Writable result = factory.newInstance();
         if (result instanceof Configurable) {
            ((Configurable) result).setConf(conf);
         }
         return result;
      } else {
         //采用传统的反射工具ReflectionUtils,创建对象
         return ReflectionUtils.newInstance(c, conf);
      }
   }
}

WritableFactories.newInstance()方法根据输入的类型查找对应的WritableFactory工厂对象,然后调用该对象的newInstance()创建对象,如果该对象是可配置的,newInstance()还会通过对象的setConf()方法配置对象。

WritableFactories提供注册机制,使得这些Writable子类可以将该工厂登记到WritableFactories的静态成员变量CLASS_TO_FACTORY中。下面是一个典型的WritableFactory工厂实现,来自于HDFS的数据块Block。其中,WritableFactories.setFactory()需要两个参数,分别是注册类对应的类对象和能够构造注册类的WritableFactory接口的实现,在下面的代码里,WritableFactory的实现是一个匿名类,其newInstance()方法会创建一个新的Block对象。

public class Block implements Writable, Comparable<Block> {
   static {
      WritableFactories.setFactory
         (Block.class,//类对象
         new WritableFactory() {//对应类的WritableFactory实现
            public Writable newInstance() { return new Block(); }
         });
   }           
  // ……
}

ObjectWritable作为一种通用机制,相当浪费资源,它需要为每一个输出写入封装类型的名字。如果类型的数量不是很多,而且可以事先知道,则可以使用一个静态类型数组来提高效率,并使用数组索引作为类型的序列化引用。GenericWritable就是因为这个目的被引入org.apache.hadoop.io包中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Hadoop 典型Writable类详解 的相关文章

  • C# 连接 SqlServer 数据库

    目录 一 创建表 二 给表添加数据 三 新建 C 项目 四 SqlServerHelper 五 连接数据库 一 创建表 首先 xff0c 新建一个数据库 Test xff0c 然后新建一个表 Users xff0c 字段名如下图 xff0c
  • org.xml.sax.SAXParseException的错误解决 2020-11-20

    span class token number 2020 span span class token operator span span class token number 11 span span class token operat
  • JS如何优雅的删除对象中的指定属性?

    要优雅的话 xff0c 使用 Lodash 的 omit 方法移除不要的属性 xff1a const object 61 a 1 b 2 c 3 const result 61 omit object a c 61 gt b 2 或者用 p
  • python 使用 isdigit 判断字符串中是否只由数字组成

    span class token operator span span class token operator span span class token operator span span class token operator s
  • 快速排序详解(Java实现)

    一 快速排序的基本思想 每一轮的排序都会将区域分割成两个独立的分区 xff0c 其中左分区的序列的所有值均会比右分区的所有值小 然后对子分区进行同样的分割操作 xff0c 最后达到整体有序 在排序的过程中 xff0c 由于已经分开的两部分的
  • A*算法路径规划之Matlab实现

    A 算法路径规划之matlab实现 A 算法是一种传统的路径规划算法 xff0c 相较于Dijkstra算法 xff0c 其引入了启发式算子 xff0c 有效的提高了路径的搜索效率 主要步骤包括 xff1a 1 xff09 设置起始点 目标
  • C语言中‘a‘和“a“有什么区别?

    1 本质区别 双引号里面的是字符串 xff0c 而单引号里面的代表字符 2 输出区别 str 61 a 输出的就是a这个字母 xff1b str 61 a 输出的测试65 3 底层区别 用单引号引起的一个字符实际上代表一个整数 xff0c
  • linux VNC客户端登陆失败

    vnc登陆出现 Unknown authentication scheme from VNC server 解决办法 xff08 建议在做操作之前重启vnc server xff0c 密码输错过多可能导致一直连接失败 xff09 https
  • win 10 mstsc连接 RemoteApp

    本文是关于mstsc客户端的配置 xff08 服务端的配置本文不描述 xff09 xff0c 前提是服务端配好 xff0c 知道RemoteApp怎么玩的 windows 2008 的mstsc有个配置 xff0c 关于程序 的tab页 但
  • 贪心算法的改进

    关于贪心算法 xff0c 请看我的上一篇博客 解决贪心算法的复杂度 为解决贪心算法的复杂度 本文提出 xff1a 通过分解极大联通子图去寻找影响力最大的节点的算法 强连通 xff1a 在有向图G中 xff0c 如果任意两个不同的顶点相互可达
  • 非关系型数据库-redis应用场景

    关系型数据库与非关系型数据库 redis的应用场景 xff1a 1 redis由于数据的读取和操作都在内存当中操作 xff0c 读写的效率较高 xff0c 所以经常被用来做数据的缓存 把一些需要频繁访问的数据 xff0c 而且在短时间之内不
  • Linux创建用户后,登录报错/usr/bin/xauth: file /home/user/.Xauthority does not exist

    错误信息如下 usr bin xauth span class token function file span home user Xauthority does not exist 错误原因 是因为添加用户时没有授权对应的目录 xff0
  • VSCode测试ES6语法

    一 VsCode使用 1 1 VsCode基本操作 Alt 43 Shift 43 F格式化代码 二 ES6新特性 2 1 let声明常量 2 2 const声明常量 只读变量 2 3 解析表达式 2 3 1 数组解构 2 3 2 对象解构
  • VNC远程连接树莓派报错问题解决(文末有惊喜呦)

    用VNC远程连接树莓派 xff0c 出现拒绝连接 问题原因 xff1a 只设置了PC端的服务器 xff0c 但是没有在树莓派端开端口 解决办法 xff1a 用putty或者xshell连接树莓派 xff0c 输入命令 xff1a vncse
  • ReadFile函数

    函数功能 xff1a 该函数从文件指针指示的位置开始从文件读数据 xff0c 在读操作完成后 xff0c 如果文件句柄不是用重叠属性来创建 xff0c 则文件指针用实际读的字数来调整 如果文件句柄为重叠的输入和输出 xff08 I O xf
  • error MSB8041: 此项目需要 MFC 库。从 Visual Studio 安装程序(单个组件选项卡)为正在使用的任何工具集和体系结构安装它们。

    最近新安装了vs2019企业版 xff0c 用vs打开vs2015的工程出现如下错误 xff1a error MSB8041 此项目需要 MFC 库 从 Visual Studio 安装程序 单个组件选项卡 为正在使用的任何工具集和体系结构
  • PC和Android模拟器之间虚拟串口通信

    前言 很多时候我们在做串口通信的时候需要去模拟串口来调试 xff0c 这样可以极大的方便我们的工作 xff0c 不然的话可能需要连接硬件设备 xff0c 比较麻烦 一般情况下我们先在电脑上模拟串口后初步调试 xff0c 最后阶段在硬件设备上
  • 【C语言】将正数转成负数,将负数转成正数

    将正数转成负数 xff0c 将负数转成正数 include lt stdio h gt int change int num if num lt 0 num 61 num 1 else if num gt 0 num 61 num 43 1
  • C语言中的输入输出流和缓冲区(重点)详解

    导读 xff1a C语言中我们用到的最频繁的输入输出方式就是scanf 与printf scanf xff1a 从标准输入设备 键盘 读取数据 xff0c 并将值存放在变量中 printf xff1a 将指定的文字 字符串输出到标准输出设备
  • MFC下按钮自绘的实现

    MFC下按钮自绘的实现 MFC下按钮自绘的实现 xff08 一 xff09 在MFC下编程 xff0c 很多时候对于标准的按钮控件不是很满意 xff0c 想要弄的美观点 这就需要按钮自绘 这里简单记录一下方法 xff0c 以免过个十天半个月

随机推荐

  • Invalidaterect()函数的作用详细解释

    函数原型 xff1a BOOLInvalidateRect HWND hWnd handle of window withchanged update region 窗口句柄 CONST RECT lpRect address ofrect
  • 【Python小白学习——错误解决3】RuntimeWarning: More than 20 figures have been opened

    待解决的问题 xff1a RuntimeWarning More than 20 figures have been opened Figures created through the pyplot interface 96 matplo
  • ffmpeg修改视频文件的分辨率

    在本文中 xff0c 我们将展示如何调整任何视频文件的大小 这种方法是在Linux系统 xff08 几乎任何发行版 xff09 中调整视频文件大小的最佳方法之一 xff0c 也是Windows和Mac用户的绝佳替代方案 更改视频文件的分辨率
  • SQLServer安装教程(史上最详细版本)

    下载 安装包已上传至百度网盘 xff0c 安装包里已含注册码 xff0c 下载地址 xff08 含注册码 xff09 链接 xff1a https pan baidu com s 147YRI7DdCqTOTKfe9UNtiA 提取码 xf
  • 通过 GetFileVersionInfo 获取EXE 或 DLL的各种资源

    查找网上资源甚多 xff0c 但无一个代码能用 xff0c 甚憾 自已阅读MSDN资料 xff0c 终于写得下面代码 xff0c 经测试通过 xff0c 若有问题请发邮件给我 xff0c 谢谢 xff01 struct LANGANDCOD
  • Tab控件的使用(二)

    tab控件其实只是提供了一组标签按钮 xff0c 其相互切换有响应的消息 xff0c 而且实现了对这些按钮管理的功能 至于通过这些标签按钮来管理我们自己的界面那就要我们通过其提供的各种方法自己实现了 xff0c 这一方面减轻了tab控件自身
  • (转载)MFC -- radio button 的用法

    本文转自http cao416451347ming blog 163 com blog static 1154556162009102795016258 先为对话框加上2个radio button xff0c 分别是Radio1和Radio
  • Linux给普通用户添加sudo权限

    在对 Linux 进行各种操作时 xff0c 为了安全起见 xff0c 一般不使用root用户直接对系统进行操作 原因 在linux中root的权限非常大 xff0c 比如我们常开玩笑的命令 rm rf xff0c 这都是可以直接执行的 而
  • Linux网络编程:socket、客户端服务器端使用socket通信(TCP)

    1 socket概念 socket xff08 套接字 xff09 xff0c 用于网络中不同主机间进程的通信 socket是一个伪文件 xff0c 包含读缓冲区 写缓冲区 socket必须成对出现 socket可以建立主机进程间的通信 x
  • MySQL client does not support authentication protocol requested by server问题解决

    问题描述 xff1a 1251 Client does not support authentication protocol reuqested by server consider upgrading MySQL client 问题分析
  • kubernetes--集群环境搭建

    本文将搭建一套master node node k8s集群环境 xff0c 需要准备三台至少cpu gt 2G mem gt 2G的虚拟机 xff0c 步骤如下 xff1a 1 在所有节点配置hosts并节点间的连接性 分别在三个节点配置h
  • 数据库服务器的安装与配置

    理论基础 数据库服务器是当今应用最为广泛的一种服务器类型 xff0c 许多企业在信息化建设过程中都要购置数据库服务器 数据库服务器主要用于存储 查询 检索企业内部的信息 xff0c 因此需要搭配专用的数据库系统 xff0c 对服务器的兼容性
  • IDEA刷新与清除缓存

    idea清除缓存 刷新项目 idea刷新与清除缓存的正确菜单 Build Project xff1a 更新Project下所有的Module Build Module xff1a 更新指定的Module Rebuild Project xf
  • VNC远程桌面安装配置

    VNC远程桌面安装配置 服务器 xff1a Linux centos 7 0 客户端 xff1a Windows 10 1 服务器安装vnc服务端2 编辑vnc配置文件3 客户端安装4 解决端口访问的问题 1 服务器安装vnc服务端 直接用
  • 第1章 计算机基础知识

    1 1 计算机的基本概念 xff08 1 xff09 计算机的发展 计算机的诞生 1946年第一台电子数字计算机ENIAC由美国宾夕法尼亚大学研制成功 它是一个庞然大物 xff0c 共有18000个电子管 1500个继电器 xff0c 耗电
  • WIN7系统下搭建Docker,部署ODL与mininet

    1 docker 安装 版本 xff1a 18 03 win7系统下和win10系统稍有不同 安装流程 xff1a https www runoob com docker windows docker install html 由于国外源问
  • Debian10中文环境配置

    在安装系统的时候会有一个语言区域选项 xff0c 不管当时选的什么 xff0c 在这里都可以更改 同时解决中文乱码问题 以下操作最好在root用户下进行 xff0c root用户操作起来方便一些 xff0c 不会被权限困扰 换源 最好在做所
  • Redis删除key

    Redis 删除 key 的方式分为两种 xff0c 一种是单独删除指定的 key xff0c 另一种是根据通配符进行批量删除 1 删除指定的 key 1 登录 Redis 客户端 redis cli 注 xff1a 如果提示 Redis
  • Jlink下载stm32cube生成的程序会出现No Cortex-M SW Device Found错误,下载的第一次没事,第二次就会报错

    Jlink下载stm32cube生成的程序会出现No Cortex M SW Device Found错误 xff0c 下载的第一次正常 xff0c 第二次就会报错的问题 作为写stm32的神器stm32cubemx越来越多人开始使用 xf
  • Hadoop 典型Writable类详解

    Hadoop 典型Writable类详解 Hadoop将很多Writable类归入org apache hadoop io包中 xff0c 在这些类中 xff0c 比较重要的有Java基本类 Text Writable集合 ObjectWr