免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
[Hadoop源碼解讀](五)MapReduce篇之Writable相關(guān)類

昨天出去玩了,今天繼續(xù)。

  前面講了InputFormat,就順便講一下Writable的東西吧,本來應(yīng)當是放在HDFS中的。

  當要在進程間傳遞對象或持久化對象的時候,就需要序列化對象成字節(jié)流,反之當要將接收到或從磁盤讀取的字節(jié)流轉(zhuǎn)換為對象,就要進行反序列化。Writable是Hadoop的序列化格式,Hadoop定義了這樣一個Writable接口。

  1. public interface Writable {  
  2.   void write(DataOutput out) throws IOException;  
  3.   void readFields(DataInput in) throws IOException;  
  4. }  

一個類要支持可序列化只需實現(xiàn)這個接口即可。下面是Writable類得層次結(jié)構(gòu),借用了<<Hadoop in Action:The Definitive Guide>>的圖。

     


下面我們一點一點來看,先是IntWritable和LongWritable。

          


WritableComparable接口擴展了Writable和Comparable接口,以支持比較。正如層次圖中看到,IntWritable、LongWritable、ByteWritable等基本類型都實現(xiàn)了這個接口。IntWritable和LongWritable的readFields()都直接從實現(xiàn)了DataInput接口的輸入流中讀取二進制數(shù)據(jù)并分別重構(gòu)成int型和long型,而write()則直接將int型數(shù)據(jù)和long型數(shù)據(jù)直接轉(zhuǎn)換成二進制流。IntWritable和LongWritable都含有相應(yīng)的Comparator內(nèi)部類,這是用來支持對在不反序列化為對象的情況下對數(shù)據(jù)流中的數(shù)據(jù)單位進行直接的,這是一個優(yōu)化,因為無需創(chuàng)建對象。看下面IntWritable的代碼片段:

  1. public class IntWritable implements WritableComparable {  
  2.   private int value;  
  3.   
  4.    //…… other methods  
  5.   public static class Comparator extends WritableComparator {  
  6.     public Comparator() {  
  7.       super(IntWritable.class);  
  8.     }  
  9.   
  10.     public int compare(byte[] b1, int s1, int l1,  
  11.                        byte[] b2, int s2, int l2) {  
  12.       int thisValue = readInt(b1, s1);  
  13.       int thatValue = readInt(b2, s2);  
  14.       return (thisValue<thatValue ? -1 : (thisValue==thatValue ? 0 : 1));  
  15.     }  
  16.   }  
  17.   
  18.   static {                                        // register this comparator  
  19.     WritableComparator.define(IntWritable.class, new Comparator());  
  20.   }  
  21. }  


代碼中的static塊調(diào)用WritableComparator的static方法define()用來注冊上面這個Comparator,就是將其加入WritableComparator的comparators成員中,comparators是HashMap類型且是static的。這樣,就告訴WritableComparator,當我使用WritableComparator.get(IntWritable.class)方法的時候,你返回我注冊的這個Comparator給我[對IntWritable來說就是IntWritable.Comparator],然后我就可以使用comparator.compare(byte[] b1, int s1, int l1,byte[] b2, int s2, int l2)來比較b1和b2,而不需要將它們反序列化成對象[像下面代碼中]。comparator.compare(byte[] b1, int s1, int l1,byte[] b2, int s2, int l2)中的readInt()是從WritableComparator繼承來的,它將IntWritable的value從byte數(shù)組中通過移位轉(zhuǎn)換出來。

  1. //params byte[] b1, byte[] b2  
  2. RawComparator<IntWritable> comparator = WritableComparator.get(IntWritable.class);  
  3. comparator.compare(b1,0,b1.length,b2,0,b2.length);  

注意,當comparators中沒有注冊要比較的類的Comparator,則會返回一個默認的Comparator,然后使用這個默認Comparator的compare(byte[] b1, int s1, int l1,byte[] b2, int s2, int l2)方法比較b1、b2的時候還是要序列化成對象的,詳見后面細講WritableComparator。

LongWritable的方法基本和IntWritable一樣,區(qū)別就是LongWritable的值是long型,且多了一個額外的LongWritable.DecresingComparator,它繼承于LongWritable.Comparator,只是它的比較方法返回值與使用LongWritable.Comparator比較相反[取負],這個應(yīng)當是為降序排序準備的。

  1. public class LongWritable implements WritableComparable {  
  2.   private long value;  
  3.   //……others  
  4.   /** A decreasing Comparator optimized for LongWritable. */   
  5.   public static class DecreasingComparator extends Comparator {  
  6.     public int compare(WritableComparable a, WritableComparable b) {  
  7.       return -super.compare(a, b);  
  8.     }  
  9.     public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {  
  10.       return -super.compare(b1, s1, l1, b2, s2, l2);  
  11.     }  
  12.   }  
  13.   static {                                       // register default comparator  
  14.     WritableComparator.define(LongWritable.class, new Comparator());  
  15.   }  
  16. }  


另外,ByteWritable、BooleanWritable、FloatWritable、DoubleWritable都基本一樣。


然后我們看VIntWritable和VLongWritable,這兩個類基本一樣而且VIntWritable[反]的value編碼的時候也是使用VLongWritable的value編解碼時的方法,主要區(qū)別是VIntWritable對象使用int型value成員,而VLongWritable使用long型value成員,這是由它們的取值范圍決定的。它們都沒有Comparator,不像上面的類。

我們只看VLongWritable即可,先看看其源碼長什么樣。

  1. public class VLongWritable implements WritableComparable {  
  2.   private long value;  
  3.   
  4.   public VLongWritable() {}  
  5.   
  6.   public VLongWritable(long value) { set(value); }  
  7.   
  8.   /** Set the value of this LongWritable. */  
  9.   public void set(long value) { this.value = value; }  
  10.   
  11.   /** Return the value of this LongWritable. */  
  12.   public long get() { return value; }  
  13.   
  14.   public void readFields(DataInput in) throws IOException {  
  15.     value = WritableUtils.readVLong(in);  
  16.   }  
  17.   
  18.   public void write(DataOutput out) throws IOException {  
  19.     WritableUtils.writeVLong(out, value);  
  20.   }  
  21.   
  22.   /** Returns true iff <code>o</code> is a VLongWritable with the same value. */  
  23.   public boolean equals(Object o) {  
  24.     if (!(o instanceof VLongWritable))  
  25.       return false;  
  26.     VLongWritable other = (VLongWritable)o;  
  27.     return this.value == other.value;  
  28.   }  
  29.   
  30.   public int hashCode() {  
  31.     return (int)value;  
  32.   }  
  33.   
  34.   /** Compares two VLongWritables. */  
  35.   public int compareTo(Object o) {  
  36.     long thisValue = this.value;  
  37.     long thatValue = ((VLongWritable)o).value;  
  38.     return (thisValue < thatValue ? -1 : (thisValue == thatValue ? 0 : 1));  
  39.   }  
  40.   
  41.   public String toString() {  
  42.     return Long.toString(value);  
  43.   }  
  44.   
  45. }  

在上面可以看到它編碼時使用WritableUtils.writeVLong()方法。WritableUtils是關(guān)于編解碼等的,暫時只看關(guān)于VIntWritable和VLongWritable的。

VIntWritable的value的編碼實際也是使用writeVLong():

  1. public static void writeVInt(DataOutput stream, int i) throws IOException {  
  2.   writeVLong(stream, i);  
  3. }  

首先VIntWritable的長度是[1-5],VLonWritable長度是[1-9],如果數(shù)值在[-112,127]時,使用1Byte表示,即編碼后的1Byte存儲的就是這個數(shù)值。{中文版權(quán)威指南上p91我看見說范圍是[-127,127],我猜可能是編碼方法進行更新了}。如果不是在這個范圍內(nèi),則需要更多的Byte,而第一個Byte將被用作存儲長度,其它Byte存儲數(shù)值。

writeVLong()的操作過程如下圖,解析附在代碼中[不知道說的夠明白不,如果感覺難理解,個人覺得其實也不一定要了解太細節(jié)]。

                         

WritableUtils.writeVLong()源碼:

  1. public static void writeVLong(DataOutput stream, long i) throws IOException {  
  2.   if (i >= -112 && i <= 127) {  
  3.     stream.writeByte((byte)i);  
  4.     return;  //-112~127 only use one byte  
  5.   }  
  6.       
  7.   int len = -112;  
  8.   if (i < 0) {  
  9.     i ^= -1L; // take one's complement' ~1 = (11111111)2  得到這  
  10.             //個i_2, i_2 + 1 = |i|,可想一下負數(shù)的反碼如何能得到其正數(shù)[連符號一起取反+1]  
  11.     len = -120;  
  12.   }  
  13.       
  14.   long tmp = i;  //到這里,i一定是正數(shù),這個數(shù)介于[0,2^64-1]  
  15.   //然后用這個循環(huán)計算一下長度,i越大,實際長度越大,偏離長度起始值[原來len]越大,len值越小  
  16.   while (tmp != 0) {   
  17.     tmp = tmp >> 8;  
  18.     len--;  
  19.   }  
  20.   //現(xiàn)在,我們顯然計算出了一個能表示其長度的值len,只要看其偏離長度起始值多少即可    
  21.   stream.writeByte((byte)len);  
  22.       
  23.   len = (len < -120) ? -(len + 120) : -(len + 112); //看吧,計算出了長度,不包含第一個Byte哈[表示長度的Byte]  
  24.       
  25.   for (int idx = len; idx != 0; idx--) {  //然后,這里從將i的二進制碼從左到右8位8位地拿出來,然后寫入流中  
  26.     int shiftbits = (idx - 1) * 8;  
  27.     long mask = 0xFFL << shiftbits;  
  28.     stream.writeByte((byte)((i & mask) >> shiftbits));  
  29.   }  
  30. }  

現(xiàn)在知道它是怎么寫出去的了,再看看它是怎么讀進來,這顯然是個反過程。

WritableUtils.readVLong():

  1. public static long readVLong(DataInput stream) throws IOException {  
  2.   byte firstByte = stream.readByte();  
  3.   int len = decodeVIntSize(firstByte);  
  4.   if (len == 1) {  
  5.     return firstByte;  
  6.   }  
  7.   long i = 0;  
  8.   for (int idx = 0; idx < len-1; idx++) {  
  9.     byte b = stream.readByte();  
  10.     i = i << 8;  
  11.     i = i | (b & 0xFF);  
  12.   }  
  13.   return (isNegativeVInt(firstByte) ? (i ^ -1L) : i);  
  14. }  
這顯然就是讀出字節(jié)表示長度[包括表示長度],然后從輸入流中一個Byte一個Byte讀出來,& 0xFF是為了不讓系統(tǒng)自動類型轉(zhuǎn)換,然后再^ -1L,也就是連符號一起取反.

WritableUtils.decodeVIntSize()就是獲取編碼長度:

  1. public static int decodeVIntSize(byte value) {  
  2.   if (value >= -112) {  
  3.     return 1;  
  4.   } else if (value < -120) {  
  5.     return -119 - value;  
  6.   }  
  7.   return -111 - value;  
  8. }  
顯然,就是按照上面圖中的反過程,使用了-119和-111只是為了獲取編碼長度而不是實際數(shù)值長度[不包含表示長度的第一個Byte]而已。


繼續(xù)說前面的WritableComparator,它是實現(xiàn)了RawComparator接口。RawComparator無非就是一個compare()方法。

  1. public interface RawComparator<T> extends Comparator<T> {  
  2.   public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2);  
  3. }  
WritableComparator是RawComparator實例的工廠[注冊了的Writable的實現(xiàn)類],它為這些Writable實現(xiàn)類提供了反序列化用的方法,這些方法都比較簡單,比較難的readVInt()和readVLong()也就是上面說到的過程。Writable還提供了compare()的默認實現(xiàn),它會反序列化才比較。如果WritableComparator.get()沒有得到注冊的Comparator,則會創(chuàng)建一個新的Comparator[其實是WritableComparator的實例],然后當你使用 public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)進行比較,它會去使用你要比較的Writable的實現(xiàn)的readFields()方法讀出value來。

比如,VIntWritable沒有注冊,我們get()時它就構(gòu)造一個WritableComparator,然后設(shè)置key1,key2,buffer,keyClass,當你使用 public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) ,則使用VIntWritable.readField從編碼后的byte[]中讀取value值再進行比較。


然后是ArrayWritable和TwoDArrayWritable,AbstractMapWritable

這兩個Writable實現(xiàn)分別是對一位數(shù)組和二維數(shù)組的封裝,不難想象它們都應(yīng)該提供一個Writable數(shù)組和保持關(guān)于這個數(shù)組的類型,而且序列化和反序列化也將使用封裝的Writable實現(xiàn)的readFields()方法和write()方法。

  1. public class TwoDArrayWritable implements Writable {  
  2.   private Class valueClass;  
  3.   private Writable[][] values;  
  4.   
  5.   //……others  
  6.   public void readFields(DataInput in) throws IOException {  
  7.     // construct matrix  
  8.     values = new Writable[in.readInt()][];            
  9.     for (int i = 0; i < values.length; i++) {  
  10.       values[i] = new Writable[in.readInt()];  
  11.     }  
  12.   
  13.     // construct values  
  14.     for (int i = 0; i < values.length; i++) {  
  15.       for (int j = 0; j < values[i].length; j++) {  
  16.         Writable value;                             // construct value  
  17.         try {  
  18.           value = (Writable)valueClass.newInstance();  
  19.         } catch (InstantiationException e) {  
  20.           throw new RuntimeException(e.toString());  
  21.         } catch (IllegalAccessException e) {  
  22.           throw new RuntimeException(e.toString());  
  23.         }  
  24.         value.readFields(in);                       // read a value  
  25.         values[i][j] = value;                       // store it in values  
  26.       }  
  27.     }  
  28.   }  
  29.   
  30.   public void write(DataOutput out) throws IOException {  
  31.     out.writeInt(values.length);                 // write values  
  32.     for (int i = 0; i < values.length; i++) {  
  33.       out.writeInt(values[i].length);  
  34.     }  
  35.     for (int i = 0; i < values.length; i++) {  
  36.       for (int j = 0; j < values[i].length; j++) {  
  37.         values[i][j].write(out);  
  38.       }  
  39.     }  
  40.   }  
  41. }  
也就是那樣,沒什么好講的了。

另外還有些TupleWritable,AbstractMapWritable->{MapWritable,SortMapWritable},DBWritable,CompressedWritable,VersionedWritable,GenericWritable之類的,有必要時去再談它們,其實也差不多,功能不一樣而已。


參考資料:

      [1]Hadoop權(quán)威指南中文版第二版
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
十二、hadoop的序列化
中國省市數(shù)據(jù)庫表——MYSql版
Hadoop學習之路(6)MapReduce自定義分區(qū)實現(xiàn)
讀寫parquet格式文件的幾種方式
Arduino之簡易航模遙控器
感知哈希算法——找出相似的圖片
更多類似文章 >>
生活服務(wù)
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服