org.apache.hadoop.io.Text.getBytes()を使用するときの注意

以前Hadoop入力データの文字エンコーディングに対応するため以下のようなコードを書いた

public class CustomMapper extends Mapper<LongWritable, Text, Text, Text> {

	@Override
	protected void map(LongWritable key, Text value, Context context)
			throws IOException, InterruptedException {
		Configuration conf = context.getConfiguration();
		String encoding = conf.get(Const.CONF_CHAR_ENCODING, "UTF-8");
		String line = new String(value.getBytes(), Charset.forName(encoding));
		// エンコードした文字列で処理
	}
}

テストしていたら変数「line」に前の行のデータが残る現象が発生
正確には同じMapタスクの直前にTextInputFormatで読み込んだデータが後ろに残る
下のような感じ

入力ファイル

AAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBB
CCCCCCCCCC
DDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEE

MapタスクAの入力

AAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBB

MapタスクBの入力

CCCCCCCCCC
DDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEE

MapタスクAの変数「line」

AAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBAAAAA

MapタスクBの変数「line」

CCCCCCCCCC
DDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEDDDDD

1.0.3のjavadocを確認するとgetBytes()で取得した値で正しいのはgetLength()の位置までと書いてある
実際に問題が発生する場合はgetBytes()で取得した配列のサイズがgetLength()の値を超えている

Text (Hadoop 1.0.3 API)

よって以下のように修正

public class CustomMapper extends Mapper<LongWritable, Text, Text, Text> {

	@Override
	protected void map(LongWritable key, Text value, Context context)
			throws IOException, InterruptedException {
		Configuration conf = context.getConfiguration();
		String encoding = conf.get(Const.CONF_CHAR_ENCODING, "UTF-8");
		byte[] lineArray = ArrayUtils.subarray(
				value.getBytes(), 0, value.getLength());
		String line = new String(lineArray, Charset.forName(encoding));
		// エンコードした文字列で処理
	}
}

2.0.1-alphaではcopyBytes()メソッドが追加されそちらを使うように書かれていた。

Text (Apache Hadoop Main 2.0.1-alpha API)