1. 程式人生 > >Hadoop 自定義Writable NullpointerException

Hadoop 自定義Writable NullpointerException

Hadoop環境:Hadoop2.4

在定義Hadoop的Writable時候,有時需要使用到陣列,而不是簡單的字串或者單個的數值。比如下面的程式碼:

package test;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.WritableComparable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyData implements WritableComparable<MyData>,Cloneable {
	private Logger log = LoggerFactory.getLogger(MyData.class);

	
	private float[] distance;
	
	public MyData(){// 空指標異常
		set(new float[6]);  // 註釋掉此行程式碼,在readFields會有空指標異常
	}
	

	public MyData(float[] distance) {
		set(distance);
	}
	public  void set(float[] distance) {
		this.distance=distance;
	}
	
	
	@Override
	public void readFields(DataInput arg0) throws IOException {
		for(int i=0;i<distance.length;i++){
			distance[i]=arg0.readFloat();
		}
	}
	@Override
	public void write(DataOutput arg0) throws IOException {
	
		for(int i=0;i<distance.length;i++){
			
			arg0.writeFloat(distance[i]);
		}
	}
	@Override
	public int compareTo(MyData o) {// 當前值小於o則返回負數
			
		float[] oDistance =o.distance;
		int cmp=0;
		for(int i=0;i<oDistance.length;i++){
			
			if(Math.abs(this.distance[i]-oDistance[i])<0.0000000001){
				continue; // 比較下一個
			}
			if(this.distance[i]<oDistance[i]){
				return -1;
			}else{
				return 1;
			}
		}
		
		return cmp;
	}
	
	
	@Override
	public int hashCode(){
		int hashCode =0;
		for(int i=0;i<distance.length;i++){
			
			hashCode=+Float.floatToIntBits(distance[i]);
		}
		return hashCode;
	}


	public float[] getDistance() {
		return distance;
	}

	public void setDistance(float[] distance) {
		this.distance = distance;
	}
	
}
可以看到其無參建構函式裡面含有一個初始化的操作,這個初始化的操作限定了其矩陣distance的維度(程式碼中設定為6),但是一般這個值由外部設定才比較合適,但是在讀取的時候,也就是readFields的時候是從這個無參建構函式進入的,這裡沒有辦法設定引數,沒有辦法在外面初始化這個矩陣的大小,但是不設定又不行,有沒有什麼辦法呢?在Mahout的一些程式碼中可以看到類似的程式碼,比如VectorWritable,從VectorWritable可以找到解決這個問題的答案。先看看vectorWritable的一段程式碼:
 @Override
  public void readFields(DataInput in) throws IOException {
    int flags = in.readByte();
    int size = Varint.readUnsignedVarInt(in);
    readFields(in, (byte) flags, size);
  }
通過這段程式碼可以知道,不一定要通過外部傳入,其實可以從in中讀取即可。具體如何做呢?定義一個數組distance的大小變數,比如為num,然後把num在write中寫入,然後在readFields中先讀出,然後再初始化陣列distance,這樣就不會有剛才的問題了。具體程式碼如下:
package test;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.WritableComparable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyData implements WritableComparable<MyData>,Cloneable {
	private Logger log = LoggerFactory.getLogger(MyData.class);

	
	private float[] distance;
	private int num;
	public MyData(){// 此處不再有空指標異常
	//	set(new float[6]);  // 註釋掉此行程式碼,在readFields不會有空指標異常
	}
	
	public MyData(float[] distance) {
		this.num=distance.length;
		set(distance);
	}
	public  void set(float[] distance) {
		this.distance=distance;
	}
	
	
	@Override
	public void readFields(DataInput arg0) throws IOException {
		num = arg0.readInt();
		distance = new float[num];
		for(int i=0;i<distance.length;i++){
			distance[i]=arg0.readFloat();
		}
	}
	@Override
	public void write(DataOutput arg0) throws IOException {
		arg0.writeInt(num);
		for(int i=0;i<distance.length;i++){
			
			arg0.writeFloat(distance[i]);
		}
	}
	@Override
	public int compareTo(MyData o) {// 當前值小於o則返回負數
			
		float[] oDistance =o.distance;
		int cmp=0;
		for(int i=0;i<oDistance.length;i++){
			
			if(Math.abs(this.distance[i]-oDistance[i])<0.0000000001){
				continue; // 比較下一個
			}
			if(this.distance[i]<oDistance[i]){
				return -1;
			}else{
				return 1;
			}
		}
		
		return cmp;
	}
	
	
	@Override
	public int hashCode(){
		int hashCode =0;
		for(int i=0;i<distance.length;i++){
			
			hashCode=+Float.floatToIntBits(distance[i]);
		}
		return hashCode;
	}


	public float[] getDistance() {
		return distance;
	}

	public void setDistance(float[] distance) {
		this.distance = distance;
	}
	
}

通過上面的改進,就不用擔心 空指標的問題了。

分享,成長,快樂