分区器和序列化

按照需求开始自定义分区器

回顾我们的需求,我们在做词频统计的时候,把a-m开头的保存在一个文件中,n-z开头的保存在另一个文件中。此时,我们就需要使用自定义分区了。

具体的流程是:

  1. 定义一个分区类。继承Partitioner类。
  2. 重写getPartition方法,它会返回一个整型的结果。结果相同的key对应的数据就会放在一个文件中。

参考代码如下:

import org.apache.hadoop.io.Text;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.mapreduce.Partitioner;

public class CustomPartitioner extends Partitioner {

    @Override

    public int getPartition(Text key, IntWritable value, int numPartitions) {

        // 根据单词的首字母分配分区

        char firstLetter = key.toString().charAt(0);

        if (firstLetter >= 'a' && firstLetter <= 'm') {

            return 0; // 分配到Reducer 0

        } else {

            return 1; // 分配到Reducer 1

        }

    }

}

代码说明:

(1)它用来将map方法生成的键值对,按照指定的规则来进行分区。这个泛型类的泛型是map方法输出的k2,v2的数据类型。

(2)它返回的分区号是从0开始,且是连续的。

(3)每个map生成的键值对,都会调用这个方法来得到它对应的分区号。

应用分区器解决需求

上一步我们定义了分区器,接下来,我们在job中使用它。需要改动的代码就是在Driver类中,添加一句setPartitionerClass,代码如下:

 WordCountDriver {
   void main(){      
          // 省略其他....

         job.setPartitionerClass(WordCountPartitioner.class);
         job.setNumReduceTasks(2)

// 6 提交

         boolean b = job.waitForCompletion(true);

         System.exit(b ? 0 : 1);

}

}

说明:这里的setNumReduceTasks的值最好与setPartitioner返回的分区器的个数一致。 如果不一致。reduceTasks > 分区器,会导致多余的reduceTasks空跑,且会生成空文件。 如果reduceTasks<分区器,导致不能并行进行。

序列化

Java自带的序列化

下面我们来看一个例子:通过代码来定义一个类,并创建它的一个对象,把这个对象保存到文件中(序列化),然后再写代码读取这个文件并还原回来。

java中的序列化需要在定义类的时候实现实现 java.io.Serializable 接口。在序列化时,使用ObjectOutputStream的writeObject方法把类写入某个文件。在反序列化时,使用ObjectInputStream的readObjec方法来还原这个类。

下面是一段示例代码。

先写一个类。

public class Student {

    public Student(String name,int age) {

        this.name = name;

        this.age = age;

    }

    String name;

    int age;

}

在java中,对应的序列化和反序列化的方法是:

  1. 让这个类实现 Serializable 接口,也就是在代码中补充implements Serializable。

public class Student implements Serializable {

    // 省略其他...

}

  1. 序列化。新建文件输出流对象,并写入要实例化的实例。

   Student student = new Student("xiaohua", 10);

   // java序列化

   ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student_java"));

   oos.writeObject(student);

   oos.close();

反序列化。通过文件输入流读入文件,并使用ObjectInputStream来进一步实例化对象,然后调用readObject来生成对象。对应的代码如下

  // 反序列化:将字节序列转换为内存中的对象

   // 1. 创建一个ObjectInputStream对象,构造方法中传入一个InputStream对象

   ObjectInputStream studentJava = new ObjectInputStream(new FileInputStream("student_java"));

   // 2. 使用ObjectInputStream对象中的readObject方法,读取文件中的对象

   Student student1 = (Student) studentJava.readObject();

   System.out.println(student1.name + " " + student1.age);

   // 3. 关闭ObjectInputStream对象

   studentJava.close();

学生类:Stduent类

public class Student implements Writable{
 public Student(String name,int age) {

        this.name = name;

        this.age = age;

    }

    public Student() { }

    public String name;

    public int age;

    @Override

    public void write(DataOutput dataOutput) throws IOException {

        dataOutput.writeUTF(name);

        dataOutput.writeInt(age);

    }

    @Override

    public void readFields(DataInput dataInput) throws IOException {

        name = dataInput.readUTF();

        age = dataInput.readInt();

    }

}

测试类:TestStudent

package com.example.serial;

import java.io.*;

public class TestStudent {

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Student student = new Student("小花", 18);

        // hadoop序列化

        DataOutputStream dos = new DataOutputStream(new FileOutputStream("Student_hadoop.txt"));

        student.write(dos);

        // hadoop 反序列化

        DataInputStream dis = new DataInputStream(new FileInputStream("Student_hadoop.txt"));

        Student student1 = new Student();

        student1.readFields(dis);

        System.out.println(student1.name+ " "+student1.age);

    }

}

你可能感兴趣的:(python,开发语言)