天天看點

關于MapReduce架構中Key-Value對象的重用

  • 問題說明

在寫MapReduce的reduce程式時,有時會用到Java中Treemap容器對準備輸出的

但是輸出時卻發現key的值都是一個,而且都是最後一個put進入treeMap中的值!這是為什麼?

請教了公司大牛wanglei2才知道其中的原因,在此感謝王老師并表達膜拜之情!

原因是Key-Value對象的重用導緻的:Key是一個引用,它在棧中,指向堆中一個對象,同樣Value也是如此。雖然reduce方法會反複執行多次,但key和value相關的對象隻有兩個,key和value的引用也是隻有兩個,reduce會反複重用這兩個對象。是以上面代碼段put進去的key指向的對象隻有一個,對象的值為最後一個put進去的值,是以輸出的key的值都為最後一個,而new LongWritable(count)是建立了一個新的對象。是以是不同的。是以如果要儲存key或者value的結果,隻能将其中的值取出另存或者重新clone一個對象(例如Text text= new Text(value) 或者 String a = value.toString()),而不能直接賦引用。因為引用從始至終都是指向同一個對象,你如果直接儲存它們,那最後它們都指向最後一個輸入記錄。會影響最終計算結果而出錯。

  • 程式驗證:
package cn.bjut.vlsi.TreeMap;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;

public class TreeMapTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO 自動生成的方法存根
        TreeMap<LongWritable,Text> treemap = new TreeMap<LongWritable, Text>();
        String[] cityName = {"北京","南京","秦皇島","濟南","天津","上海","蘇州"};
        int i = ;
        Text key = new Text();
        for(String str : cityName){
            key.set(str);
            i++;
            treemap.put(new LongWritable(i), key);
        }       
        System.out.println("******************TreeMap Output******************");   
        Set<Entry<LongWritable,Text>> entrySet = treemap.entrySet();
        for(Entry<LongWritable,Text> ent : entrySet){
            System.out.println(ent.getValue() + " " + ent.getKey());        
        }
    }
}
程式輸出:
           

蘇州 1

蘇州 2

蘇州 3

蘇州 4

蘇州 5

蘇州 6

蘇州 7

建立put中key的對象,程式改為:

package cn.bjut.vlsi.TreeMap;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;

public class TreeMapTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO 自動生成的方法存根
        TreeMap<LongWritable,String > treemap = new TreeMap<LongWritable, String >();//*****修改****//
        String[] cityName = {"北京","南京","秦皇島","濟南","天津","上海","蘇州"};
        int i = ;
        Text key = new Text();
        for(String str : cityName){
            key.set(str);
            i++;
            treemap.put(new LongWritable(i), key.toString());//*****修改****//
        }       
        System.out.println("******************TreeMap Output******************");   
        Set<Entry<LongWritable,String >> entrySet = treemap.entrySet();//*****修改****//
        for(Entry<LongWritable,String > ent : entrySet){
            System.out.println(ent.getValue() + " " + ent.getKey());        
        }
    }
}
           

程式輸出:

北京 1

南京 2

秦皇島 3

濟南 4

天津 5

上海 6

蘇州 7