序列化就是將內(nèi)存中的對(duì)象,轉(zhuǎn)換成字節(jié)序列(或者按照其他數(shù)據(jù)傳輸協(xié)議轉(zhuǎn)換),以便于持久化存儲(chǔ)到磁盤中以及網(wǎng)絡(luò)傳輸
一般情況下,對(duì)象只存儲(chǔ)在本地的內(nèi)存中,只允許本地的進(jìn)程調(diào)用。而隨著分布式程序的出現(xiàn),需要在不同的主機(jī)上不同進(jìn)程調(diào)用對(duì)象,這就需要將對(duì)象通過(guò)網(wǎng)絡(luò)傳輸?shù)搅硗獾闹鳈C(jī)上。但是對(duì)象不經(jīng)過(guò)處理無(wú)法通過(guò)網(wǎng)絡(luò)傳輸,而通過(guò)序列化處理之后,對(duì)象可以通過(guò)網(wǎng)絡(luò)傳輸了。
java中自行實(shí)現(xiàn)了序列化方案,只要定義一個(gè)類的時(shí)候?qū)崿F(xiàn) Serializable 接口,那么java內(nèi)部就會(huì)自動(dòng)實(shí)現(xiàn)相應(yīng)的序列化。如:
public class Test implements Serializable{ //這個(gè)序列化號(hào)是必須的,用于標(biāo)識(shí)該類 private static final long serialVersionUID = xxxx;}
但是由于Java中的序列化接口實(shí)現(xiàn)的時(shí)候,會(huì)附帶很多額外的信息,如各種校驗(yàn)信息,header,繼承體系等。不便于在網(wǎng)絡(luò)上高效傳輸(性能不高)。所以hadoop自己額外實(shí)現(xiàn)了序列化的機(jī)制,體積短小,占用帶寬低,序列化和反序列化快速
hadoop中以實(shí)現(xiàn) Writable 這個(gè)接口的類,就可以序列化。而且hadoop實(shí)現(xiàn)了許多基本類型的可序列化的類。依賴圖如下所示:
圖 2.1 hadoop序列化依賴圖
可以看到所有的可序列化的類都實(shí)現(xiàn)了 WritableComparable 這個(gè)接口,這個(gè)接口同時(shí)繼承了 Writable 以及 Comparable 接口。下面看看這個(gè)這三個(gè)接口:
//WritableComparable.javapublic interface WritableComparable<T> extends Writable, Comparable<T> {}/*空的接口*///Writable.javapublic interface Writable { void write(DataOutput var1) throws IOException; void readFields(DataInput var1) throws IOException;}/*主要包含讀和寫序列化對(duì)象的方法*///Comparable.javapublic interface Comparable<T> { public int compareTo(T o);}/*提供序列化對(duì)象間比較的方法*/
java類型 | hadoop writable類型 |
---|---|
boolean | BooleanWritable |
byte | ByteWritable |
Int | IntWritable |
float | FloatWritable |
long | LongWritable |
double | DoubleWritable |
string | Text |
map | MapWritable |
array | ArrayWritable |
下面挑個(gè)IntWritable這個(gè)常用的序列化類來(lái)看看源碼
package org.apache.hadoop.io;import java.io.DataInput;import java.io.DataOutput;import java.io.IOException;import org.apache.hadoop.classification.InterfaceAudience.Public;import org.apache.hadoop.classification.InterfaceStability.Stable;@Public@Stablepublic class IntWritable implements WritableComparable<IntWritable> { private int value; public IntWritable() { } public IntWritable(int value) { this.set(value); } public void set(int value) { this.value = value; } public int get() { return this.value; } //這里是實(shí)現(xiàn)了 writable 接口的方法 public void readFields(DataInput in) throws IOException { this.value = in.readInt(); } public void write(DataOutput out) throws IOException { out.writeInt(this.value); } //序列化對(duì)象的equals比較方法 public boolean equals(Object o) { if (!(o instanceof IntWritable)) { return false; } else { IntWritable other = (IntWritable)o; return this.value == other.value; } } public int hashCode() { return this.value; } //比較對(duì)象大小的方法 public int compareTo(IntWritable o) { int thisValue = this.value; int thatValue = o.value; return thisValue < thatValue ? -1 : (thisValue == thatValue ? 0 : 1); } public String toString() { return Integer.toString(this.value); } /*這里是關(guān)鍵,將下面的Comparator內(nèi)部類作為默認(rèn)的比較方法。 因?yàn)檫@里采用靜態(tài)代碼塊的方式,所以只要該類載入時(shí),就會(huì)執(zhí)行該代碼塊,直接創(chuàng)建 Comparator對(duì)象,后面無(wú)需通過(guò)外部類創(chuàng)建對(duì)象的方式來(lái)調(diào)用 compare方法,因?yàn)閷?duì)象已經(jīng)提前創(chuàng)建好了。比起上的 compareTo 方法,還要手動(dòng)創(chuàng)建一個(gè)外部類對(duì)象才能調(diào)用 compareTo 方法,這里可以直接調(diào)用,效率要快。 */ static { WritableComparator.define(IntWritable.class, new IntWritable.Comparator()); } //這個(gè)內(nèi)部類也實(shí)現(xiàn)了 compare比較方法 public static class Comparator extends WritableComparator { public Comparator() { super(IntWritable.class); } public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { int thisValue = readInt(b1, s1); int thatValue = readInt(b2, s2); return thisValue < thatValue ? -1 : (thisValue == thatValue ? 0 : 1); } }}
其他short,long的序列化類的實(shí)現(xiàn)也是類似的。
要點(diǎn):
(1)必須實(shí)現(xiàn) Writable接口
(2)必須有無(wú)參構(gòu)造方法,因?yàn)榉葱蛄谢瘯r(shí)需要反射調(diào)用無(wú)參構(gòu)造方法
(3)重寫序列化方法
public void write(DataOutput out) throws IOException{ //DataOutput接口中定義了每個(gè)基本類型序列化的方法,這里以Long為例 out.writeLong(upFlow); out.writeLong(downFlow); out.writeLong(sumFlow);}
(4)重寫反序列化方法
public void readFields(DataInput in) throws IOException{ upFlow = in.readLong(); downFlow = in.readLong(); sumFlow = in.readLong();}
(5)序列化寫入和反序列化讀取時(shí)要注意,寫入和讀取的順序必須完全一致
(6)按照需要可以重寫 toSting 方法,便于保存在文件中的內(nèi)容
(7)如果該自定義序列化類是作為鍵值對(duì)中的key使用的話,因?yàn)镸apReduce中會(huì)以key進(jìn)行排序,那么就會(huì)涉及到 key 的比較問(wèn)題。所以需要實(shí)現(xiàn) Comparable 接口。而該接口就得實(shí)現(xiàn) compareTo 方法
public int compareTo(Test o) { return (-1 | 0 |1 ); 表示小于,等于,大于三種結(jié)果}
首先屬性中的自定義類也是需要實(shí)現(xiàn)序列化接口的。所以下面的DateDimension和ContactDimension都是已經(jīng)實(shí)現(xiàn)序列化的了。
public class ComDimension extends BaseDimension { private DateDimension dateDimension = new DateDimension(); private ContactDimension contactDimension = new ContactDimension();//序列化就直接調(diào)用類的write方法即可,按照下面的形式 @Override public void write(DataOutput dataOutput) throws IOException { this.dateDimension.write(dataOutput); this.contactDimension.write(dataOutput); } @Override public void readFields(DataInput dataInput) throws IOException { this.dateDimension.readFields(dataInput); this.contactDimension.readFields(dataInput); }}
來(lái)源:https://www.icode9.com/content-4-532901.html
聯(lián)系客服