4

私はジョブの map reduce パイプラインの構築に取り組んでいます (ある MR ジョブの出力を別のジョブに入力としてフィードします)。渡される値はかなり複雑で、さまざまなタイプのリストと値を持つハッシュ マップがリストとして存在します。Hadoop api には ListWritable がないようです。ジェネリック型を作成しようとしていますが、クラス型自体を渡さない限り、readFields 実装でジェネリック型をインスタンス化できないようです。

public class ListWritable<T extends Writable> implements Writable {
    private List<T> list;
    private Class<T> clazz;

    public ListWritable(Class<T> clazz) {
       this.clazz = clazz;
       list = new ArrayList<T>();
    }

    @Override
    public void write(DataOutput out) throws IOException {
        out.writeInt(list.size());
        for (T element : list) {
            element.write(out);
        }
     }

     @Override
     public void readFields(DataInput in) throws IOException{
     int count = in.readInt();
     this.list = new ArrayList<T>();
     for (int i = 0; i < count; i++) {
        try {
            T obj = clazz.newInstance();
            obj.readFields(in);
            list.add(obj);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
      }
    }
}

しかし、hadoop では、値を読み戻すために、すべての書き込み可能オブジェクトに引数なしのコンストラクターが必要です。誰かが同じことを試みて、この問題を解決しましたか? ティア。

4

2 に答える 2

2

私はこのような書き込み可能なものの広範なライブラリを持っていますが、型を常に自己境界ジェネリック型の抽象クラスとして宣言し、それを使用するときはすべての型が具象化された自明なサブクラスを宣言します。Hadoop はひどくリフレクション ベースであるため、ほとんどの場合、実際のジョブではジェネリックを使用しない方がよいでしょう。ただし、ジェネリックは、ジョブの I/O タイプのスーパータイプとして非常に便利です。

例えば

public abstract class AbstractListWritable< T extends Writable & Cloneable, U extends AbstractListWritable< T, U > >
implements Writable {
    T tCursor;
    List< T > ltBacking;
    protected AbstractListWritable( T tCursor ) {
        this.tCursor = tCursor.clone();
        this.ltBacking = new ArrayList< T >();
    }
    ...
    @Override
    public void readFields(DataInput in) throws IOException {
        int count = in.readInt();
        this.ltBacking.clear();
        for (int i = 0; i < count; i++) {
            tCursor.readFields(in);
            list.add(tCursor.clone());
        }
    }
}

public class TextListWritable extends AbstractListWritable< Text, TextListWritable > {
    public TextListWritable() {
        super( new Text() );
    }
}

トーマスの答えは異種リストを許可しますが、ジェネリック型ではリストを作成できず、ほとんどの場合 I/O が Hadoop のボトルネックであるため、その戦略はお勧めしません。

于 2012-07-03T08:11:03.630 に答える
0

各レコード内にクラス名を記述する必要がありますが、これは非常に冗長なので、強力な型付けをお勧めします。

ただし、コードを次のように変更する必要があります。

@Override
public void write(DataOutput out) throws IOException {
    out.writeUTF(clazz.getName());
    out.writeInt(list.size());
    for (T element : list) {
        element.write(out);
    }
 }

 @Override
 public void readFields(DataInput in) throws IOException{
 clazz = Class.forName(in.readUTF());
 int count = in.readInt();
 this.list = new ArrayList<T>();
 for (int i = 0; i < count; i++) {
    try {
        T obj = clazz.newInstance();
        obj.readFields(in);
        list.add(obj);
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
  }
}

次に、引数なしのコンストラクターも提供できます。ただし、レコードごとのオーバーヘッドとしてクラス名を UTF-8 文字列として使用します。

于 2012-07-03T07:57:34.120 に答える