回頭看了看之前自定義的UDF,UDAF,UDTF,竟然有種生疏的感覺,因此,對於其中的代碼重新做了註釋,更加的詳細和容易理解,下面就是我自己定義的幾個樣例,比較簡單,主要是通過樣例來了解如何自定義UDF來完成需求。
1、UDAF
需求是找出指定字段的topN,數據類型定義爲double,下面是實現代碼。
package com.wangl.hadoop.udf;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
@SuppressWarnings("deprecation")
public class TopNUDAF extends UDAF{
public static class State{
ArrayList a;//保存topn的結果
int n;//調用該函數的topN的N
boolean ascending;//升序還是降序
}
public static class Evaluator implements UDAFEvaluator{
private State state;
public Evaluator(){
init();
}
//初始化Evaluator對象
public void init() {
if(state == null){
state = new State();
}
state.a = new ArrayList();
state.n = 0;
state.ascending = false;//默認降序
}
/**
* map任務每行都會調用一次,iterate接收的參數正是調用函數時傳入的參數
* @param other 聚合的字段值
* @param n topN的N
* @return
* @throws UDFArgumentException
*/
public boolean iterate(Double other,int n,String asc) throws UDFArgumentException{
//升降序topn表示,false表示最大值,true表示最小值
if(asc == null || asc.equals("")){
throw new UDFArgumentException("third argument like 'asc' or 'desc'");
}
if(!asc.equals("asc") && !asc.equals("desc")){
throw new UDFArgumentException("third argument like 'asc' or 'desc'");
}
if(asc.equals("asc")){
state.ascending = true;
}
state.n = n;
if(other != null){
//是否插入標示
boolean doInsert = state.a.size() < n;
//如果當前的state.a的元素數量小於n時則需要插入操作
//如果當前的state.a的元素數量大於或者等於n時,則需要將數組中的最後一位與待插入的數進行比較
//從而確定是否應該插入。
if(!doInsert){
Double last = state.a.get(state.a.size()-1);
if(state.ascending){
//當ascending爲true時,即爲升序,topN中即爲最小的N位,因此只有待插入的數比最後一位小時,纔會進行插入操作,否則直接返回
doInsert = other < last;
}else{
//當ascending爲false時,即爲降序,topN中即爲最大的N位,因此只有待插入的數比最後一位大時,纔會進行插入操作,否則直接返回
doInsert = other > last;
}
}
if(doInsert){
//有順序的插入other的值
binaryInsert(state.a,other,state.ascending);
if(state.a.size() > n){
state.a.remove(state.a.size()-1);
}
}
}
return true;
}
//將value得知按照ascending的順序插入到list中相應的位置
static > void binaryInsert(List list, T value, boolean ascending){
//根據順序獲取value在list中的位置,當list中沒有元素的時候,則會返回-1,插入方法是先根據二分法進行查找,然後再插入
int position = Collections.binarySearch(list, value, getComparator(ascending,(T)null));
if(position < 0 ){
position = (-position) - 1;
}
list.add(position,value);
}
//比較器
static > Comparator getComparator(boolean ascending,T dummy){
Comparator comp;
if(ascending){
//如果是升序,誰小誰在前
comp = new Comparator(){
public int compare(T o1,T o2){
return o1.compareTo(o2);
}
};
}else{
//如果是降序,誰大誰排在前
comp = new Comparator(){
public int compare(T o1,T o2){
return o2.compareTo(o1);
}
};
}
return comp;
}
//返回局部topN
public State terminatePartial(){
if(state.a.size()>0){
return state;
}else{
return null;
}
}
/**
* reduce端,將map的輸出結果進行合併
* @param s 將要與當前State合併的State
* @return
*/
public boolean merge(State s){
if(s != null){
state.n = s.n;
state.a = sortedMerge(s.a,state.a,state.ascending,s.n);
}
return true;
}
static > ArrayList sortedMerge(List list1,List list2,boolean ascending,int n){
Comparator comp = getComparator(ascending, (T)null);
int n1 = list1.size();
int n2 = list2.size();
int p1 = 0;//當前list1的元素
int p2 = 0;//當前list2的元素
//保存結果,有n個元素
ArrayList output = new ArrayList(n);
//遍歷並將list1和list2歸併到output 中,歸併過程中保留output最多有n個元素
while(output.size() terminate(){
if(state.a.size()>0){
return state.a;
}else{
return null;
}
}
}
}
需求是將表中的日期格式轉換成指定的模式:
package com.wangl.hadoop.udf;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class MyDateParse extends UDF{
private Text result = new Text();
//hive自定義函數,繼承UDF之後,還需要定義一個evaluate方法
public Text evaluate(Text str){
String newStr = str.toString();
//根據實際情況制定日期模式,根據字段的內容對字段進行轉換
SimpleDateFormat format = new SimpleDateFormat("dd/MMMMM/yyyy:HH:mm:ss Z", Locale.ENGLISH);
if(str.toString().indexOf("[")>-1){
newStr = newStr.replace("[", "");
}
if(str.toString().indexOf("]")>-1){
newStr = newStr.replace("]", "");
}
try{
Date date = format.parse(newStr);
SimpleDateFormat newFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
result.set(newFormat.format(date));
return result;
}catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
3、UDAF
需求是將表中的兩個字段按照指定的分隔符進行連接
package com.wangl.hadoop.udf;
import java.util.ArrayList;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.StringObjectInspector;
public class MergeFields extends GenericUDTF{
String str1;
String str2;
Object[] forwardObj = null;
@Override
public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
if(argOIs.length!=2){
throw new UDFArgumentException("參數異常");
}
//初始化輸入輸出的格式
forwardObj = new Object[1];
str1 = ((StringObjectInspector)argOIs[0]).getPrimitiveJavaObject(argOIs[0]);
str2 = ((StringObjectInspector)argOIs[1]).getPrimitiveJavaObject(argOIs[1]);
ArrayList fieldNames = new ArrayList();
ArrayList fieldOIs = new ArrayList();
fieldNames.add("rcol1");
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
//指定輸出的字段名稱和輸出內容格式
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
}
/**
* 這裏只是對兩個字段進行連接,主要是瞭解如何自定義UDTF的方法
*/
@Override
public void process(Object[] args) throws HiveException {
forwardObj[0] = str1 +"---"+ str2;
forward(forwardObj);
}
@Override
public void close() throws HiveException {
// TODO Auto-generated method stub
}
}
以上的代碼僅供參考,如有問題歡迎指出。