回头看了看之前自定义的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; } } } }
2、UDF
需求是将表中的日期格式转换成指定的模式:
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
}
}
以上的代码仅供参考,如有问题欢迎指出。