2012-10-09 85 views
5

的可寫接口假設我有一個枚舉:枚舉值實現Hadoop的

public enum SomeEnumType implements Writable { 
    A(0), B(1); 

    private int value; 

    private SomeEnumType(int value) { 
    this.value = value; 
    } 

    @Override 
    public void write(final DataOutput dataOutput) throws IOException { 
    dataOutput.writeInt(this.value); 
    } 

    @Override 
    public void readFields(final DataInput dataInput) throws IOException { 
    this.value = dataInput.readInt(); 
    } 
} 

我想通過它的一個實例作爲一些其他類的實例的一部分。

等於不起作用,因爲它不會考慮枚舉的內部變量,而且所有枚舉實例在編譯時都是固定的,並且不能在別處創建。

這是否意味着我無法通過Hadoop中的線路發送枚舉或者有解決方案?

回答

0

我不知道Hadoop的東西,但基於接口的the documentation,你也許可以做到這樣的:在Hadoop的枚舉

public void readFields(DataInput in) throws IOException { 
    // do nothing 
} 

public static SomeEnumType read(DataInput in) throws IOException { 
    int value = in.readInt(); 
    if (value == 0) { 
     return SomeEnumType.A; 
    } 
    else if (value == 1) { 
     return SomeEnumType.B; 
    } 
    else { 
     throw new IOException("Invalid value " + value); 
    } 
} 
+0

儘管靜態方法是自定義寫入任意文件的很好解決方案,但您必須填充接口方法,因爲它們在內部調用。可編寫的作品,如Java API中的Externalizable。 –

4

我的正常和最佳的解決方案是通過序列化枚舉他們的序數值。

public class EnumWritable implements Writable { 

    static enum EnumName { 
     ENUM_1, ENUM_2, ENUM_3 
    } 

    private int enumOrdinal; 

    // never forget your default constructor in Hadoop Writables 
    public EnumWritable() { 
    } 

    public EnumWritable(Enum<?> arbitraryEnum) { 
     this.enumOrdinal = arbitraryEnum.ordinal(); 
    } 

    public int getEnumOrdinal() { 
     return enumOrdinal; 
    } 

    @Override 
    public void readFields(DataInput in) throws IOException { 
     enumOrdinal = in.readInt(); 
    } 

    @Override 
    public void write(DataOutput out) throws IOException { 
     out.writeInt(enumOrdinal); 
    } 

    public static void main(String[] args) { 
     // use it like this: 
     EnumWritable enumWritable = new EnumWritable(EnumName.ENUM_1); 
     // let Hadoop do the write and read stuff 
     EnumName yourDeserializedEnum = EnumName.values()[enumWritable.getEnumOrdinal()]; 
    } 

} 

顯然,它也有缺點:序數會發生變化,因此,如果您交換ENUM_2ENUM_3和閱讀以前序列化的文件,這會返回錯誤的其他枚舉。

所以,如果你知道枚舉類事前,你可以寫你的枚舉的名稱,並使用它像這樣:

enumInstance = EnumName.valueOf(in.readUTF()); 

這將使用稍多的空間,但它節省更多的變化,以您的枚舉名稱。

完整的例子是這樣的:

public class EnumWritable implements Writable { 

    static enum EnumName { 
     ENUM_1, ENUM_2, ENUM_3 
    } 

    private EnumName enumInstance; 

    // never forget your default constructor in Hadoop Writables 
    public EnumWritable() { 
    } 

    public EnumWritable(EnumName e) { 
     this.enumInstance = e; 
    } 

    public EnumName getEnum() { 
     return enumInstance; 
    } 

    @Override 
    public void write(DataOutput out) throws IOException { 
     out.writeUTF(enumInstance.name()); 
    } 

    @Override 
    public void readFields(DataInput in) throws IOException { 
     enumInstance = EnumName.valueOf(in.readUTF()); 
    } 

    public static void main(String[] args) { 
     // use it like this: 
     EnumWritable enumWritable = new EnumWritable(EnumName.ENUM_1); 
     // let Hadoop do the write and read stuff 
     EnumName yourDeserializedEnum = enumWritable.getEnum(); 

    } 

} 
+1

除此之外 - 如果您在Key中使用枚舉,**不要**使用enum.hashCode()作爲您的鍵的hashCode方法的一部分 - 枚舉的hashCode的實現是默認的本地實現這或多或少是枚舉類型內存中的地址(這是JVM的依賴關係,當從不同的映射器輸出相同的密鑰時會給你帶來問題 - 它們會散列到潛在的不同的縮減器) –

+0

正是這樣,你希望使用'Writable'類型並實現哈希碼,並根據枚舉的序號/名稱進行equals。 –

+0

所以,回到這個問題 - 對於枚舉來說是不可能的。只能通過助手類。 –

1

WritableUtils有方便的方法,使它更簡單。

WritableUtils.writeEnum(dataOutput,enumData); 
enumData = WritableUtils.readEnum(dataInput,MyEnum.class);