1. 序列化反序列化
1.1 序列化反序列化是什麼
序列化: 是将記憶體中的對象轉換稱為位元組序列,以便持久化到磁盤中或用于網絡傳輸
反序列化: 将位元組序列轉換稱為記憶體中的對象
1.2 JDK 序列化反序列化
Java 中,如果需要序列化,需要實作
java.io.Serializable
接口,利用序列化版本ID
serialVersionUID
來辨別版本。
JDK 需要借助
ObjectOutputStream
(
writeObject(Object)
)和
ObjectInputStream
(
readObject()
)分别實作序列化反序列化。
1.3 Hadoop 序列化反序列化
Hadoop 不适用 JDK 序列化,是因為利用 Serializable 序列化後會附帶很多額外的資訊,不便于資料的網絡傳輸。
Hadoop 實作的序列化機制為
Writable
。
1.4 Hadoop 序列化的意義
Hadoop 在叢集之間進行通訊或者 RPC 調用的時候,需要序列化,而且要求序列化要快,且體積要小,占用帶寬要小。是以必須了解 Hadoop 的序列化機制。
序列化和反序列化在分布式資料處理領域經常出現:程序通信和永久存儲。然而Hadoop中各個節點的通信是通過遠端調用(RPC)實作的,那麼就不能使用 JDK 序列化,是以 Hadoop 自己實作了一套序列化機制。
Hadoop 序列化特點:
- 緊湊:高校使用存儲空間
- 快速:讀寫資料的額外開銷小
- 可擴充:原始序列化方式支援新協定的封包
- 互操作:支援多種語言的互動
1.5 序列化類型
Java 類型 | Hadoop Writable 類型 |
---|---|
| |
| |
| |
| |
| |
| |
| |
| |
| |
2. Writable 自定義實作
Writable 用于在對象和位元組序列之間做轉換。
自定義實作需要注意幾點:
- 必須要實作 Writable 接口
- 需要提供無參構造,應為反序列化需要反射調用空參構造函數
- 重寫序列、反序列方法,且序列和反序列的順序保持一緻
- 如果後期需要指定 key 排序,還需要實作 Comparable 接口
2.1 窺見源碼
Writable
接口中隻有兩個方法
-
:用于序列化操作write
-
:用于反序列化操作readFields
public interface Writable {
void write(DataOutput var1) throws IOException;
void readFields(DataInput var1) throws IOException;
}
如果需要比較,我們可以直接實作
WritableComparable
接口,該接口隻是繼承了兩個接口類,是以還需要實作 Comparable 的
compareTo
方法。
@Public
@Stable
public interface WritableComparable<T> extends Writable, Comparable<T> {
}
public interface Comparable<T> {
public int compareTo(T o);
}
2.2 自定義 Bean
比如:對日志檔案進行相關資訊統計
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class WritableUser implements WritableComparable<WritableUser> {
long id;
String name;
int age;
boolean sex;
int money;
// 必須實作 Writable 接口 ========================================
/**
* Writable 序列化方法
* @param dataOutput 輸出流 用來将類的成員屬性 按序輸出為二進制資料
* @throws IOException
*/
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeLong(this.id);
dataOutput.writeUTF(this.name);
dataOutput.writeInt(this.age);
dataOutput.writeBoolean(this.sex);
dataOutput.writeInt(this.money);
}
/**
* Writable 反序列化方法
* @param dataInput 輸入流 将讀取到的每一個值指派到自己的成員屬性
* @throws IOException
*/
@Override
public void readFields(DataInput dataInput) throws IOException {
this.id = dataInput.readLong();
this.name = dataInput.readUTF();
this.age = dataInput.readInt();
this.sex = dataInput.readBoolean();
this.money = dataInput.readInt();
}
/**
* 比較方法,當 age 一緻時,根據 money 降序排列
* @param o
* @return
*/
@Override
public int compareTo(WritableUser o) {
if (this.age == o.age){
return -Integer.compare( this.money, o.money);
} else {
return -Integer.compare( this.age, o.age);
}
}
// 提供無參構造,以便反序列化通過反射調用
public WritableUser() {
}
public WritableUser(long id, String name, int age, boolean sex, int money) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.money = money;
}
// 提供 set 方法,友善後期使用
public void set(long id, String name, int age, boolean sex, int money) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.money = money;
}
@Override
public String toString() {
return "WritableUser{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", money=" + money +
'}';
}
}
3. 寫在最後
隻需要序列化時,實作
Writable
,如果還需要排序,可實作
WritableComparable
。
如果想要降序排列,在傳回值前加個負号(
-
)
❤️ END ❤️