package com.github.stakafum.mapreduce; import java.util.*; /** * @author takafumi * * MapフェーズとSuffleフェーズのデータを保持し続けるためのクラス * * @param <InputMapKey> Mapフェーズの入力におけるキーのクラス * @param <InputMapValue> Mapフェーズの入力におけるバリューのクラス  * @param <IntermediateKey> Mapフェーズの出力におけるキーのクラス * @param <IntermediateValue> Mapフェーズの出力におけるバリューのクラス */ public class InputData<InputMapKey extends Comparable<InputMapKey>, InputMapValue, IntermediateKey extends Comparable<IntermediateKey>, IntermediateValue>{ //初始的Key,Value输入键值对 List<KeyValue<InputMapKey, InputMapValue>> initialKeyValue; //经过map函数计算过后的Key,Value临时键值对 List<KeyValue<IntermediateKey , IntermediateValue>> mappedKeyValue; //经过分组聚合后的Key,Value临时键值对 List<GroupedKeyValue<IntermediateKey, IntermediateValue>> gKVList; InputData(){ this.initialKeyValue = new ArrayList<KeyValue<InputMapKey, InputMapValue>>(); this.mappedKeyValue = new ArrayList<KeyValue<IntermediateKey , IntermediateValue>>(); this.gKVList = new ArrayList<GroupedKeyValue<IntermediateKey, IntermediateValue>>(); } /** * ユーザから渡された入力キーバリューをリストに格納するための関数 * @param key 入力キー * @param value 入力バリュー */ void putKeyValue(InputMapKey key, InputMapValue value){ //底层使用ArrayList有序的数组列表,列表的每个元素都有确定的索引 this.initialKeyValue.add(new KeyValue<InputMapKey, InputMapValue>(key, value)); } /** * * @param inputlist */ void reloadKeyValueList(List<KeyValue<InputMapKey, InputMapValue>> inputlist){ this.initialKeyValue = inputlist; } /** * 根据索引获取输入的Key. * 因为加入到initialKeyValue中的是KeyValue对象,包含了Key,Value. * 而initialKeyValue使用数组索引,数组的特征是索引. 根据索引可以得到数组中的每个元素即KeyValue对象. * @param index * @return */ InputMapKey getMapKey(int index){ return this.initialKeyValue.get(index).getKey(); } InputMapValue getMapValue(int index){ return this.initialKeyValue.get(index).getValue(); } int getMapSize(){ return this.initialKeyValue.size(); } /** * 经过map函数计算后的结果,加入到mappedKeyValue中. * map函数的计算结果也封装成KeyValue键值对. 每一条数据数据经过map函数计算后都能得到计算结果. * 只不过计算结果的类型可能会和原始的输入类型不一样. * 唯一确定的是: 一条输入数据经过map后一定能得到一条输出结果. * @param k map计算后中间结果的key * @param v map计算后中间结果的value */ void setMap(IntermediateKey k, IntermediateValue v){ this.mappedKeyValue.add(new KeyValue<IntermediateKey, IntermediateValue>(k, v)); } /* * Mapフェーズの後のkey-valueを表示 */ void showMap(){ for(int i = 0; i < mappedKeyValue.size(); i++){ System.out.println(mappedKeyValue.get(i).getKey().toString() + "," + mappedKeyValue.get(i).getValue().toString()); } } /* * Suffle */ int getShuffleSize(){ return this.mappedKeyValue.size(); } /* * initial_mapとinitial_reduceのメモリ解放 */ void initialRelease(){ this.initialKeyValue = null; } /* * Mapをソートする * Keyに従ってソートするが同時にValueについてもソートを行う * Shuffle的过程是将Map的输出结果进行排序. */ void cSort(){ Collections.sort(this.mappedKeyValue); } // 分组 : 将map的输出结果按照key进行分组 // 分组后的结果会作为reduce的输入 // gKVList类似于Map<Key, List<Value>> 当key不存在时,要新建List来存放后面要加进来的Value; // 当key存在时,直接往List中添加Value. void grouping(){ IntermediateKey tmpKey; GroupedKeyValue<IntermediateKey, IntermediateValue> gkv; //第一个元素的key IntermediateKey conKey = this.mappedKeyValue.get(0).getKey(); gkv = new GroupedKeyValue<IntermediateKey, IntermediateValue>(conKey, //把第一个KeyValue作为分组后的value list的第一个元素 new GroupedValues<IntermediateValue>(this.mappedKeyValue.get(0).getValue())); //因为mappedKeyValue中的KeyValue的key可能不同,也可能相同.因此要找出相同的key,归为一类 for(int i = 1; i < this.mappedKeyValue.size(); i++){ tmpKey = this.mappedKeyValue.get(i).getKey(); //key相同,加入GroupedKeyValue中 if(tmpKey.equals(gkv.getKey())){ gkv.addValue(this.mappedKeyValue.get(i).getValue()); } //key不同 else{ //TODO 注意: 因为KeyValue在分组之前经过了cSort排序阶段. 即key是按照顺序排列的. //所以一旦key不同,就把前一个GroupedKeyValue加入到列表中.因为前一个key对应的所有value都已经处理完毕了! this.gKVList.add(gkv); //和上面for循环外的第一个元素的方法类似 conKey = tmpKey; gkv = new GroupedKeyValue<IntermediateKey, IntermediateValue>(conKey, //把key不同后,找到的第一个KeyValue的值作为这个key分组计算后的第一个元素 new GroupedValues<IntermediateValue>(this.mappedKeyValue.get(i).getValue())); } } //最后还有一次加入到gKVList. 比如排序后分组前的情景: (for循环从第二个元素开始处理,其中分号表示一次循环,最后的语句是下面add) //[k1,v1],[k2,v1],[k2,v2] k2->else:add(K1); k2->if; k2->add(K2) //[k1,v1],[k1,v2],[k2,v1] k1->if; k2->else:add(K1); k2->add(K2) this.gKVList.add(gkv); this.mappedKeyValue = null; } void showSuffle(){ for(GroupedKeyValue<IntermediateKey, IntermediateValue> g_kv :gKVList ){ System.out.print(g_kv.getKey().toString()); for(IntermediateValue value : g_kv.getValues()){ System.out.print("," + value.toString()); } System.out.println(""); } } //Reduce操作的key,value是Group后的计算结果 IntermediateKey getReduceKey(int index){ return this.gKVList.get(index).getKey(); } GroupedValues<IntermediateValue> getReduceValues(int index){ return this.gKVList.get(index).getValues(); } int getReduceSize(){ return this.gKVList.size(); } }