022_Hadoop中的数据类型(Writable、WritableComparable、Comparator、RawComparator…)

来源:互联网 发布:梦幻邮箱数据 编辑:程序博客网 时间:2024/05/10 08:02
1、 在hadoop中所有的key/value都必须实现Writable接口,有两个方法,分别用于读(反序列化)和写(序列化)操作。

参考代码:

  1 package org.dragon.hadoop.mapreduce.app;  2   3 import java.io.DataInput;  4 import java.io.DataOutput;  5 import java.io.IOException;  6   7 import org.apache.hadoop.io.Writable;  8   9 /** 10  *  11  * @author ZhuXY 12  * @time 2016-3-10 下午3:49:55 13  *  14  */ 15 public class DataWritable implements Writable { 16  17     // telsphone 18  19     // upload 20     private int upPackNum; 21     private int upPayLoad; 22  23     // download 24     private int downPackNum; 25     private int downPayLoad; 26  27     public DataWritable() { 28  29     } 30  31     public void set(int upPackNum, int upPayLoad, int downPackNum, 32             int downPayload) { 33         this.upPackNum = upPackNum; 34         this.upPayLoad = upPayLoad; 35         this.downPackNum = downPackNum; 36         this.downPayLoad = downPayload; 37  38     } 39  40     public int getUpPackNum() { 41         return upPackNum; 42     } 43  44     public int getUpPayLoas() { 45         return upPayLoad; 46     } 47  48     public int getDownPackNum() { 49         return downPackNum; 50     } 51  52     public int getDownPayload() { 53         return downPayLoad; 54     } 55  56     @Override 57     public void write(DataOutput out) throws IOException { 58         out.writeInt(upPackNum); 59         out.writeInt(upPayLoad); 60         out.writeInt(downPackNum);         61         out.writeInt(downPayLoad); 62     } 63  64     /** 65      * 讀出的順序要和寫入的順序相同 66      */ 67     @Override 68     public void readFields(DataInput in) throws IOException { 69         // TODO Auto-generated method stub 70         this.upPackNum = in.readInt(); 71         this.upPayLoad = in.readInt(); 72         this.downPackNum = in.readInt(); 73         this.downPayLoad = in.readInt(); 74     } 75  76     @Override 77     public String toString() { 78         return upPackNum + "\t" + upPayLoad + "\t" + downPackNum + "\t" 79                 + downPayLoad; 80     } 81  82     @Override 83     public int hashCode() { 84         final int prime = 31; 85         int result = 1; 86         result = prime * result + downPackNum; 87         result = prime * result + downPayLoad; 88         result = prime * result + upPackNum; 89         result = prime * result + upPayLoad; 90         return result; 91     } 92  93     @Override 94     public boolean equals(Object obj) { 95         if (this == obj) 96             return true; 97         if (obj == null) 98             return false; 99         if (getClass() != obj.getClass())100             return false;101         DataWritable other = (DataWritable) obj;102         if (downPackNum != other.downPackNum)103             return false;104         if (downPayLoad != other.downPayLoad)105             return false;106         if (upPackNum != other.upPackNum)107             return false;108         if (upPayLoad != other.upPayLoad)109             return false;110         return true;111     }112 113 }
简单继承Writable例子 Code

 

2、所有的key必须实现Comparable接口,在MapReduce过程中需要对Key/Value对进行反复的排序。默认情况下依据Key进行排序的,要实现comparaTo()方法。所以通过Key既要实现Writable接口又要实现Comparable接口,Hadoop中提供了一个公共的接口,叫做WritableComparable接口:

3、由于需要序列化反序列化和进行比较,对java对象需要重写一下几个方法:

①    equals();

②    hashCode();

③    toString()方法

如IntWritable类型的实现:

 1 package org.apache.hadoop.io; 2  3 import java.io.*; 4  5 /** A WritableComparable for ints. */ 6 public class IntWritable implements WritableComparable { 7   private int value; 8  9   public IntWritable() {}10 11   public IntWritable(int value) { set(value); }12 13   /** Set the value of this IntWritable. */14   public void set(int value) { this.value = value; }15 16   /** Return the value of this IntWritable. */17   public int get() { return value; }18 19   public void readFields(DataInput in) throws IOException {20     value = in.readInt();21   }22 23   public void write(DataOutput out) throws IOException {24     out.writeInt(value);25   }26 27   /** Returns true iff <code>o</code> is a IntWritable with the same value. */28   public boolean equals(Object o) {29     if (!(o instanceof IntWritable))30       return false;31     IntWritable other = (IntWritable)o;32     return this.value == other.value;33   }34 35   public int hashCode() {36     return value;37   }38 39   /** Compares two IntWritables. */40   public int compareTo(Object o) {41     int thisValue = this.value;42     int thatValue = ((IntWritable)o).value;43     return (thisValue<thatValue ? -1 : (thisValue==thatValue ? 0 : 1));44   }45 46   public String toString() {47     return Integer.toString(value);48   }
View Code

4、数据类型,必须有一个无参的构造方法,为了方便反射创建对象。

在自定义数据类型中,建议使用java原生数据类型,最好不要使用hadoop对原生类型封装好的数据类型,即如下样例代码:

推荐使用:

不建议使用:

5、问题:

  当数据写入磁盘时,如果要进行排序的话,需要首先从磁盘中读取数据进行反序列化成对象,然后在内存中对反序列化的对象进行比较。

  对字节(未经过反序列化字节)进行直接比较,不需要进行反序列化以后再比较呢?如果要实现上述功能,Hadoop数据类型需要实现一个接口RawComparator。

  在Hadoop中有一个针对Writable数据类型,进行实现的一个通用实现类WritableComparator类。所有的数据类型,只需要继承通用类,再去需要具体功能复写相应的compara()方法。一下以IntWritable为例,查看一下:

    对于自定义的Comparator类需要以下几步:

1) 推荐Comparator类定义在数据类型内部,静态内部类,实现WritableComparator类。

  2) 重写默认无参构造方法,方法内必须调用父类有参构造方法,如下截图:

3) 重载父类的compare()方法,依据具体功能覆写。

4) 向WritableComparator类中注册自定义的Comparator类,代码如下:

 

5、自定义数据类型

  样例代码:

  1 package org.dragon.hadoop.mr.io;  2   3 import java.io.DataInput;  4 import java.io.DataOutput;  5 import java.io.IOException;  6 import org.apache.hadoop.io.WritableComparable;  7 import org.apache.hadoop.io.WritableComparator;  8 import org.apache.hadoop.io.WritableUtils;  9  10 /** 11  * 自定义数据类型对Writable的实现。 12  * 快捷键get、set选择器alt+shift+s 13  * @author ZhuXY 14  * @time 2016-3-9 下午10:40:02 15  *  16  */ 17 /** 18  * 1、Hadoop之——数据类型 19     1)    在hadoop中所有的key/value都必须实现Writable接口,有两个方法,分别用于读(反序列化)和写(序列化)操作。 20     2)    所有的key必须实现Comparable接口,在MapReduce过程中需要对Key/Value对进行反复的排序。默认情况下依据Key进行排序的,要实现comparaTo()方法。所以通过Key既要实现Writable接口又要实现Comparable接口,Hadoop中提供了一个公共的接口,叫做WritableComparable接口 21      3)    由于需要序列化反序列化和进行比较,对java对象需要重写一下几个方法: 22         ①    equals(); 23         ②    hashCode(); 24         ③    toString()方法 25     4)    数据类型,必须有一个无参的构造方法,为了方便反射创建对象。 26     5)    在自定义数据类型中,建议使用java原生数据类型,最好不要使用hadoop对原生类型封装好的数据类型,即 27       28  */ 29  30 /** 31  * 问题: 32     当数据写入磁盘时,如果要进行排序的话,需要首先从磁盘中读取数据进行反序列化成对象,然后在内存中对反序列化的对象进行比较。 33  * 对字节(未经过反序列化字节)进行直接比较,不需要进行反序列化以后再比较呢?如果要实现上述功能,Hadoop数据类型需要实现一个接口RawComparator。 34             在Hadoop中有一个针对Writable数据类型,进行实现的一个通用实现类WritableComparator类。所有的数据类型,只需要继承通用类,再去需要具体功能复写相应的compara()方法。 35      对于自定义的Comparator类需要以下几步: 36     1)    推荐Comparator类定义在数据类型内部,静态内部类,实现WritableComparator类。 37     2)    重写默认无参构造方法,方法内必须调用父类有参构造方法,如下截图: 38       39     3)    重载父类的compare()方法,依据具体功能覆写。 40     4)    向WritableComparator类中注册自定义的Comparator类,代码如下: 41  42  */ 43  44 /** 45  * WritableCOmparator是RawComparator对WritableComparable类的一个通用实现。它提供两个主要的功能。 46  * 首先他提供了一个默认的对原始compare()函数的调用,对从数据流对要比较的对象进行反序列化,然后调用对象 47  * 的compare方法。 48  * 其次,他充当的是RawComparator实例的工厂方法(Writable方法已经注册)。 49  * @author ZhuXY 50  * 51  */ 52 public class PairWritable implements WritableComparable<PairWritable> { 53  54     private String name;// Text 55     private Integer age;// IntWritale 56  57     public PairWritable() { 58     } 59  60     public PairWritable(String name, Integer age) { 61         this.set(name, age); 62     } 63  64     public void set(String name, Integer age) { 65         this.name = name; 66         this.age = age; 67     } 68  69     public String getName() { 70         return name; 71     } 72  73     public Integer getAge() { 74         return age; 75     } 76  77     /** 78      * write方法是在写入数据时调用,进行序列化 79      */ 80     @Override 81     public void write(DataOutput out) throws IOException { 82         out.writeUTF(name); 83         out.writeInt(age); 84     } 85  86     /** 87      * readField()方法是在取出数据时调用的方法,反序列化方法 88      * 以便生成对象 89      */ 90     @Override 91     public void readFields(DataInput in) throws IOException { 92         this.name = in.readUTF(); 93         this.age = in.readInt(); 94     } 95      96     /** 97      *    98         hashCode 的常规协定是:    99         1)在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。   100         2)如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。   101         3)以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。   102         4)实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)   103           104         5)当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。  105      */106     @Override107     public int hashCode() {108         return name.hashCode() * 31 + age.hashCode();109     }110 111     @Override112     public boolean equals(Object obj) {113         if (obj instanceof PairWritable) {114             PairWritable pairWritable = (PairWritable) obj;115 116             return this.name.equals(pairWritable.getName())117                     && this.age.equals(pairWritable.getAge());118         }119         return false;120     }121     122     @Override123     public String toString() {124         // TODO Auto-generated method stub125         return this.name+"\t"+this.age;126     }127     128     @Override129     public int compareTo(PairWritable o) {130         int cmp=this.name.compareTo(o.getName());131          if (cmp!=0) {132             return cmp;133         }134          return this.age.compareTo(o.getAge());135     }136     137     public static class Comparator extends WritableComparator{138         139         public Comparator(){140             super(PairWritable.class);141         }142         143         /**144          * 第一个字节数组145          * byte[] b1, int s1, int l1, 146          * 字节数组起始位置长度147          * 148          * 第二个字节数组149          * byte[] b2, int s2, int l2150          * 字节数组的起始位置长度151          */152         /**153          * 154          * 核心:155          *         这个接口允许执行者比较从流中读取的未被反序列化为对象的记录,从而省去了创建对象的所有开销。156          *         例如,IntWritables的comparator使用原始的compare()方法从每个字节数组的指定157          *         开始位置(S1和S2)和长度(L1和L2)读取整数(b1和b2),然后直接进行比较。158          */159         @Override160         public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {161             int n1=WritableUtils.decodeVIntSize(b1[s1]);162             int n2=WritableUtils.decodeVIntSize(b2[s2]);163             164             int cmp=WritableComparator.compareBytes(b1, s1+n1, l1-n1, b2, s2+n2, l2+n2);165             166             if (0!=cmp) {167                 return cmp;168             }169             170             int thisValue=readInt(b1, l1-s1-n1);171             int thatValue=readInt(b2, l2-s2-n2);172             173             return (thisValue<thatValue ?-1:(thisValue==thatValue?0:1));174         }175         static {176             WritableComparator.define(PairWritable.class, new Comparator());177         }178     }179 }
PairWritable Code包括Writable和RawComparator

  通常情况下,实现一个静态方法read(DataInput),用于构造数据类型的实例对象,方法内部调用readFields(DataInput)方法

    Hadoop MapReduce Data Type中所有的Key,必须实现WritableComparable接口,官方文档说明如下:

  比较器RawComparator,官方文档说明如下:

 

6、注意NullWritable类型

 1 package org.apache.hadoop.io; 2  3 import java.io.*; 4  5 /** Singleton Writable with no data. */ 6 public class NullWritable implements WritableComparable { 7  8   private static final NullWritable THIS = new NullWritable(); 9 10   private NullWritable() {}                       // no public ctor11 12   /** Returns the single instance of this class. */13   public static NullWritable get() { return THIS; }14   15   public String toString() {16     return "(null)";17   }18 19   public int hashCode() { return 0; }20   public int compareTo(Object other) {21     if (!(other instanceof NullWritable)) {22       throw new ClassCastException("can't compare " + other.getClass().getName() 23                                    + " to NullWritable");24     }25     return 0;26   }27   public boolean equals(Object other) { return other instanceof NullWritable; }28   public void readFields(DataInput in) throws IOException {}29   public void write(DataOutput out) throws IOException {}30 31   /** A Comparator &quot;optimized&quot; for NullWritable. */32   public static class Comparator extends WritableComparator {33     public Comparator() {34       super(NullWritable.class);35     }36 37     /**38      * Compare the buffers in serialized form.39      */40     public int compare(byte[] b1, int s1, int l1,41                        byte[] b2, int s2, int l2) {42       assert 0 == l1;43       assert 0 == l2;44       return 0;45     }46   }47 48   static {                                        // register this comparator49     WritableComparator.define(NullWritable.class, new Comparator());50   }51 }
View Code

 

0 0