【软件工程实践】Pig项目4-Data目录源码分析-Tuple3

  2021SC@SDUSC

上篇我们分析了Tuple(接口)和AbstractTuple(抽象类),这两个都是抽象方法,我们继续分析它的实现DefaultTuple

 相关资料链接:【Pig源码分析】谈谈Pig的数据模型 -数据库-火龙果软件工程

 类之间关系如下

从上图可以知道,在DefaultTuple类中有List<Object> mFields字段,这便是存储Tuple数据的地方了,mFields所持有类型为ArrayList<Object>()

 DefaultTuple代码如下

函数功能:元组的默认实现。这个类将由DefaultTupleFactory创建

构造函数:

默认构造函数。这个构造函数是公共的,所以hadoop可以直接调用它。不过,inside pig不应该调用这个函数。使用TupleFactory代替。分配后,时间复杂度:O(1)

 public DefaultTuple() {
        mFields =
new ArrayList<Object>();
    }

构造一个具有已知字段数量的元组。包级别,以便调用者不能直接调用它。生成的元组被预填充为空元素。分配后,时间复杂度:O(N)@param大小 在元组中分配的字段数量

DefaultTuple(int size) {
        mFields =
new ArrayList<Object>(size);
       
for (int i = 0; i < size; i++)
            mFields.add(
null);
    }

从现有的对象列表构造一个元组。包级别,以便调用者不能直接调用它。
加上分配后输入对象迭代的运行时间,时间复杂度:O(N)

@param c要转换为元组的对象列表

DefaultTuple(List<Object> c) {
        mFields =
new ArrayList<Object>(c);
    }

 从现有的对象列表构造一个元组。包级别,以便调用者不能直接调用它。
时间复杂度O(1) @param c要转换为元组的对象列表。这个列表将作为元组的一部分保存。@param junk只是用来区别于上面复制列表的构造函数

DefaultTuple(List<Object> c, int junk) {
        mFields = c;
    }

函数:

@Override
   
public int size() {
       
return mFields.size();
    }

获取给定字段中的值。

 public int size() {
       
return mFields.size();
    }

以列表的形式获取元组中的所有字段。

     @Override
   
public List<Object> getAll() {
       
return mFields;
    }

在给定字段中设置值。

     @Override
   
public void set(int fieldNum, Object val) throws ExecException {
        mFields.set(fieldNum, val);
    }

将字段附加到元组。这种方法效率不高,因为它可能会强制复制现有数据以实现增长数据结构。只要有可能,你就应该用newTuple(int)方法构造Tuple,然后填充set()的值中,而不是用newTuple()构造它并附加值。

     @Override
   
public void append(Object val) {
        mFields.add(val);
    }

确定元组在内存中的大小。这是数据包用来确定其内存大小的。这并不需要准确的,但它应该是一个体面的估计。

     @Override
   
public long getMemorySize() {
        Iterator<Object> i = mFields.iterator();
       
// mfields大小的其余固定部分在empty_tuple_size中计算
        long mfields_var_size = SizeUtil.roundToEight(4 + 4 * mFields.size());
       
// java hotspot 32bit vm中,最小元组大小似乎是96,可能来自这个数组列表的最小大小 
        mfields_var_size = Math.max(40, mfields_var_size);

       
// fixed overhead = 48 bytes
        //8 - tuple object header
        //8 - mFields reference
        //32 - mFields array list fixed size
       
long sum = 48 + mfields_var_size;
       
while (i.hasNext()) {
            sum += SizeUtil.getPigObjMemSize(i.next());
        }
       
return sum;
    }

是否完全相等

@Override
   
public int compareTo(Object other)

笔记:前面这几个函数都是非常简单的后面的开始难了,这个有个被定义成static的函数,没有给出函数功能的注释,从名字上看是用于比较的?

public static class DefaultTupleRawComparator extends WritableComparator implements TupleRawComparator 

继承自一个接口,一个类,引入部分如下,接口在同一个包

import org.apache.hadoop.io.WritableComparator;

hadoop源码项目没有给,TupleRawComparator如下

/**检查比较的元组中是否有一个有空字段。这个方法只有在

*{@link #compare(byte[]int,int,byte[]int,int)}返回的值为0(即元组被确定为0*/

public interface TupleRawComparator extends RawComparator,Configurable {
    
    

    public boolean hasComparedTupleNull();

}

我们回来看DefaultTupleRawComparator内的函数

public DefaultTupleRawComparator() {
           
super(DefaultTuple.class);
        }

        @Override
       
public Configuration getConf() {
           
return null;
        }

        @Override
       
public void setConf(Configuration conf)

        @Override
       
public boolean hasComparedTupleNull() {
           
return mHasNullField;
        }

将两个defaulttuple比较为原始字节。我们假设元组不是PigNullableWritable,所以是客户端类需要处理NullIndex
        @Override
       
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
            ByteBuffer bb1 = ByteBuffer.wrap(b1, s1, l1);
            ByteBuffer bb2 = ByteBuffer.wrap(b2, s2, l2);
           
int rc = compareDefaultTuple(bb1, bb2, true); // FIXME adjust for secondary sort asc
           
return rc;
        }

将两个defaulttuple比较为原始字节
        private int compareDefaultTuple(ByteBuffer bb1, ByteBuffer bb2, boolean topLevel)

这段程序很长,主要逻辑整理如下,首先比较大小相同,如果大小相同,逐个字段进行比较(这里需要case 每一种出现的类型,这里的全部类型是hadoop支持的全部类型)

功能和上面类似?是对父类的实现
        @Override
       
public int compare(Object o1, Object o2) {
            NullableTuple nt1 = (NullableTuple) o1;
            NullableTuple nt2 = (NullableTuple) o2;
           
int rc = 0;

           
// if either are null, handle differently
           
if (!nt1.isNull() && !nt2.isNull()) {
                rc = compareTuple((Tuple) nt1.getValueAsPigType(), (Tuple) nt2.getValueAsPigType());
            }
else {
               
// for sorting purposes two nulls are equal
               
if (nt1.isNull() && nt2.isNull())
                    rc =
0;
               
else if (nt1.isNull())
                    rc = -
1;
               
else
                   
rc = 1;
               
if (mWholeTuple && !mAsc[0])
                    rc *= -
1;
            }
           
return rc;
        }

比较Tuple
 private int compareTuple(Tuple t1, Tuple t2) {
            int sz1 = t1.size();
            int sz2 = t2.size();
            if (sz2 < sz1) {
                return 1;
            } else if (sz2 > sz1) {
                return -1;
            } else {
                for (int i = 0; i < sz1; i++) {
                    try {
                        int c = DataType.compare(t1.get(i), t2.get(i));
                        if (c != 0) {
                            if (!mWholeTuple && !mAsc[i])
                                c *= -1;
                            else if (mWholeTuple && !mAsc[0])
                                c *= -1;
                            return c;
                        }
                    } catch (ExecException e) {
                        throw new RuntimeException("Unable to compare tuples", e);
                    }
                }
                return 0;
            }
        }

    }

自此,DefaultTupleRawComparator程序结束了,功能为支持对父类的各种类型的比较功能(还有几个函数功能不清楚,没有父类源码)

计算hashCode的长度?推测功能和前面估计内存大小的函数差不多
@Override
    public int hashCode() {
        int hash = 17;
        for (Iterator<Object> it = mFields.iterator(); it.hasNext();) {
            Object o = it.next();
            if (o != null) {
                hash = 31 * hash + o.hashCode();
            }
        }
        return hash;
    }

输出tuple
@Override
    public void write(DataOutput out) throws IOException {
        out.writeByte(DataType.TUPLE);
        int sz = size();
        out.writeInt(sz);
        for (int i = 0; i < sz; i++) {
            DataReaderWriter.writeDatum(out, mFields.get(i));
        }
    }

 从输入流构造tuple,不过这个作者建议不用append这里又用了
@Override
    public void readFields(DataInput in) throws IOException {
        // Clear our fields, in case we're being reused.
        mFields.clear();

        // Make sure it's a tuple.
        byte b = in.readByte();
        if (b != DataType.TUPLE) {
            int errCode = 2112;
            String msg = "Unexpected data while reading tuple " + "from binary file.";
            throw new ExecException(msg, errCode, PigException.BUG);
        }
        // Read the number of fields
        int sz = in.readInt();
        for (int i = 0; i < sz; i++) {
            try {
                append(DataReaderWriter.readDatum(in));
            } catch (ExecException ee) {
                throw ee;
            }
        }
    }

返回类名
public static Class<? extends TupleRawComparator> getComparatorClass() {
        return DefaultTupleRawComparator.class;
    }

自此就是DefaultTuple全部内容了,整个程序给我的感觉就是:实际上存储的内容只有mFields,其他所有程序是构造、比较,以及为了实现可用hadoop的父类调用而去实现对hadoop中类型的支持。这样做的效果就是代码很冗长,但是支持性很好,如果有新的类型引入,直接新添加类或者继承这个类就行了,不需要修改之前写的代码。评价还是那句话,从项目层面可以便于拓展,便于协作,但是继承不得不实现父类未定义的方法即使用不上。

这里,我们终于将Pig的Data类型中的Tuple完全结束了,下一篇我们将研究Tuple的上一层,Bag

Guess you like

Origin blog.csdn.net/Aulic/article/details/120923649