Hadoop define sus propios tipos de objetos a partir de los tipos primitivos de Java. Todos ellos heredan de la interfaz WritableComparable, que a su vez hereda de la interfaz Writable y que permiten la Serialización (conversión de los datos en bytes para poder ser transmitidos por red o para su escritura en almacenes persistentes).
Algunos de los tipos son:
- IntWritable ints
- Text strings
- DoubleWritable doubles
- FloatWritable floats
- LongWritable longs
- ByteWritable bytes
- NullWritable
![]() |
Jerarquía de Clases (Hadoop the Definitive Guide) |
De los tipos vistos, aunque son propios de Hadoop, nos son más o menos conocidos, todos excepto quizas el NullWritable. Este tipo de objeto se utiliza para cuando queremos eliminar la key o el value y que Hadoop lo pueda reconocer.
Por ejemplo, si en una salida de un Job MapReduce es de tipo <Text, Text> y tenemos la key siempre a nulo, entonces el fichero de salida tendrá un formato similar a:
[separador] mivalor1 [separador] mivalor2 ...siendo [separador] el carácter de separación (por defecto una tabulación).
Y si la key la definiésemos como NullWritable, la salida sería con este formato:
mivalor1 mivalor2 ...Lo cual nos da un fichero de salida más compacto y más apropiado para usarlo como entrada de otro Job MapReduce.
Durante los desarrollos de aplicaciones Hadoop, en algún momento tendremos la necesidad de crear nuestros propios objetos.
Por ejemplo, imaginemos que queremos como objeto los datos de una persona (nombre y los dos apellidos), sería fácil crear un objeto de tipo Text:
Text persona = new Text (nombre+" "+primerApellido+" "+segundoApellido);
Como veis, separamos los datos con un espacio, y luego al hacer:
String[] listaPersonas = persona.toString().split(" ");
Podría ser un problema si uno de los apellidos fuera compuesto y contuviera espacios.
Así que lo mejor sería crear tu propio objeto.
Es muy importante saber que las keys deben implementar siempre la interfaz WritableComparable, y los values la interfaz Writable.
La interfaz Writable
Contiene los métodos de serialización y deserialización readFields y write:
public interfaz Writable{ void readFields(DataInput in); void write(DataOutput out); }Ejemplo:
public class PersonaWritable implements Writable { Text nombre, primerApellido, segundoApellido; public PersonaWritable() { this.nombre = new Text(); this.primerApellido = new Text(); this.segundoApellido = new Text(); } public PersonaWritable(Text nombre, Text primerApellido, Text segundoApellido) { this.nombre = nombre; this.primerApellido = primerApellido; this.segundoApellido = segundoApellido; } @Override public void readFields(DataInput arg0) throws IOException { this.nombre.readFields(arg0); this.primerApellido.readFields(arg0); this.segundoApellido.readFields(arg0); } @Override public void write(DataOutput arg0) throws IOException { this.nombre.write(arg0); this.primerApellido.write(arg0); this.segundoApellido.write(arg0); } // Implementar getters y setters }
La interfaz WritableComparable
Además de contener los métodos de serialización y deserialización readFields y write, debe implementar los métodos compareTo, hashCode y equals, ya que extiende, además de la interfaz Writable, de la Comparable < >.
Ejemplo:
public class PersonaWritableComparable implements WritableComparable<PersonaWritableComparable>{ Text nombre, primerApellido, segundoApellido; public PersonaWritableComparable() { this.nombre = new Text(); this.primerApellido = new Text(); this.segundoApellido = new Text(); } public PersonaWritableComparable(Text nombre, Text primerApellido, Text segundoApellido) { this.nombre = nombre; this.primerApellido = primerApellido; this.segundoApellido = segundoApellido; } @Override public void readFields(DataInput arg0) throws IOException { this.nombre.readFields(arg0); this.primerApellido.readFields(arg0); this.segundoApellido.readFields(arg0); } @Override public void write(DataOutput arg0) throws IOException { this.nombre.write(arg0); this.primerApellido.write(arg0); this.segundoApellido.write(arg0); } @Override public int compareTo(PersonaWritableComparable o) { if(this.nombre.compareTo(o.nombre) != 0){ return this.nombre.compareTo(o.nombre); }else if(this.primerApellido.compareTo(o.primerApellido) != 0){ return this.primerApellido.compareTo(o.primerApellido); }else if(this.segundoApellido.compareTo(o.segundoApellido) != 0){ return this.segundoApellido.compareTo(o.segundoApellido); } return 0; } @Override public boolean equals(Object obj) { if(obj instanceof PersonaWritable){ PersonaWritableComparable p = (PersonaWritableComparable) obj; return this.nombre == p.nombre && this.primerApellido == p.primerApellido && this.segundoApellido == p.segundoApellido; } return false; } @Override public int hashCode() { return this.nombre.hashCode()*163 + this.primerApellido.hashCode()*163 + this.segundoApellido.hashCode()*163; } // Implementar getters y setters }
Si en algún momento quieres desarrollar un tipo propio y la salida (output) va a ser con este tipo, también hará falta implementar el método toString()
Si tuviéramos que serializar objetos binarios, se haría a través de arrays de bytes
El método write sería:
- Serializar el objeto en un array de bytes
- Escribir el número de bytes
- Escribir el array de bytes
Y el método readFields sería:
- Leer el número de bytes
- Crear un array de bytes de ese tamaño
- Leer el array
- Deserializar el objeto
Ver también: Ejemplo de uso de Tipos de Datos propios con las interfaces Writable y WritableComparable
No hay comentarios:
Publicar un comentario
Gracias por dejar vuestras sugerencias, dudas, críticas o comentarios en general