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 }
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 }
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 }
通常情况下,实现一个静态方法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 "optimized" 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 }