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

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     @Override
107     public int hashCode() {
108         return name.hashCode() * 31 + age.hashCode();
109     }
110 
111     @Override
112     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     @Override
123     public String toString() {
124         // TODO Auto-generated method stub
125         return this.name+"\t"+this.age;
126     }
127     
128     @Override
129     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 l2
150          * 字节数组的起始位置长度
151          */
152         /**
153          * 
154          * 核心:
155          *         这个接口允许执行者比较从流中读取的未被反序列化为对象的记录,从而省去了创建对象的所有开销。
156          *         例如,IntWritables的comparator使用原始的compare()方法从每个字节数组的指定
157          *         开始位置(S1和S2)和长度(L1和L2)读取整数(b1和b2),然后直接进行比较。
158          */
159         @Override
160         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 ctor
11 
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 comparator
49     WritableComparator.define(NullWritable.class, new Comparator());
50   }
51 }
View Code

 

你可能感兴趣的:(022_Hadoop中的数据类型(Writable、WritableComparable、Comparator、RawComparator…))