T004: Tipos Genéricos y Java Framework Collections

publicado a la‎(s)‎ 22 may. 2012 15:22 por Hernan Nina Hanco   [ actualizado el 4 jun. 2013 11:03 ]

I) Objetivo

  • Conocer el uso de Tipos Genéricos y Java Framework Collections.


II) Marco conceptual


Generics Type

Permite que un objeto pueda usarse uniformemente como parámetro en distintos contextos o situaciones. Las clases genéricas o paramétricas permiten definir clases que no se quiere o puede especificar completamente hasta determinar un tipo de dato. En estas clases el tipo concreto de datos que manipulan se especifica como un parámetro en el momento en que se crean los objetos de la clase.

Los tipos genéricos tratan de abstraernos de algún detalle y permitirnos trabajar a un mayor nivel de abstracción.


Comparable en Java

Comparable es una interfaz que sólo contiene un método: compareTo, que nos sirve para ordenar los valores según un orden natural, bien sea numérico o alfabético. El método compareTo devuelve un int, que puede ser 1, -1 o 0.


Iterator


Los objetos de tipo iterator permiten recorrer colecciones. Disponen de un conjunto de métodos que permiten avanzar sobre la colección y obtener los objetos de ésta durante un recorrido para su tratamiento.


El framework de Collections

El framework de Collections fue incorporado a Java en la versión 1.2 por el arquitecto Joshua Bloch, uno de los principales gurús de Java. En Java 5 este framework sufrió una revisión bastante fuerte, el último trabajo que Bloch realizó en Sun Microsystems antes de irse a trabajar para Google, que consiste en soportar tipos de datos genéricos. Este framework se construye sobre las seis interfaces que, junto con sus relaciones jerárquicas, se muestran en la siguiente figura.

Collection

Se trata de una de las dos clases raíz de la jerarquía. Representa cualquier colección de objetos que pertenezcan a un mismo tipo, y proporciona operaciones para añadir y eliminar objetos de la colección, recorrer la colección, conocer el tamaño de la colección, etcétera.


Interfaz List

Esta interfaz representa una colección de datos ordenada en la cual se admiten datos duplicados y sobre la cual el usuario tiene control respecto a en qué posición de la lista se encuentra cada elemento. Podríamos pensar en este contenedor como en una lista doblemente enlazada, donde cada elemento tiene un elemento anterior y un elemento siguiente y donde, por tanto, la posición de cada elemento dentro de la lista está bien definida. Para dar soporte a las operaciones que permiten añadir un elemento en una posición determinada de la lista, borrar un elemento que se encuentre en una posición determinada, añadir una colección de elementos después de una posición determinada.

Las dos implementaciones más comunes de la interfaz List son ArrayList y LinkedList


La interfaz Set


La interfaz Set no añade ningún método respecto a la interfaz Collection, pero se usa para representar conjuntos de elementos que no admiten duplicados. Por tanto, un Set necesita conocer si los objetos que contiene son o no iguales entre sí. Existen dos implementaciones principales de esta interfaz: HashSet y TreeSet


Map en Java


Un mapa es una estructura de Java que nos permite almacenar pares clave/valor. De tal manera que para una clave solamente tenemos un valor. Si añadimos un nuevo elemento clave/valor cuando la clave ya existe, se sobrescribe el valor almacenado anteriormente. La estructura a listar que utilizamos como mapa es un HashMap. Lo primero que tenemos que hacer es crear el mapa y añadirle elementos:

Map<String,String> hm = new HashMap<String,String>();

hm.put("1","Luis");
hm.put("2","Amaya");
hm.put("3","Julio");

Si intentamos añadir otro elemento con una clave ya existente, sobreescribe el valor. Ya que no puede haber elementos duplicados. 

Para recorrerlo nos apoyaremos sobre un Iterator que será el puntero encargado en recorrer los elementos del mapa. Obtendremos un iterador sobre la estructura invocando los métodos .entrySet() e .iterator(). Usar iteradores permite separar la lógica de la clase de la lógica de la iteración. Los iteradores tienen un comportamiento similar a los enumerados. Para mover el iterador utilizaremos el método .next() y si queremos comprobar que el iterador ha llegado al final de la estructura tenemos el método .hasNext()

Iterator it = hm.entrySet().iterator();
while (it.hasNext()) { 
    Map.Entry e = (Map.Entry)it.next();
    System.out.println(e.getKey() + " " + e.getValue());
}

De la estructura recuperaremos los valores mediante .getKey(), para la clave y .getValue(), para el valor.

III) Prácticas

1) Ejemplo de Tipos Genéricos o polimorfismo parametrizado

En el presente programa la clase "Concatenador" utiliza un tipo genérico identificado con "E" y encerrado entre el signo "<>" después del nombre de la clase. Esto da la posibilidad de declarar "dato1" y "dato2" del tipo genérico "E". Esto significa mas adelante cuando se invoque objetos de la clase "Concatenar" podemos reemplazar el tipo genérico "E" por cualquier tipo de datos concreto (Ejem. Integer, Float, Persona, etc.).

package miselania.tiposgenericos;

/**
*
* @author hernan
*/
public class Concatenador<E> {
   private E dato1;
   private E dato2;
   public String toString() {
       return "El primer dato es: " + getDato1()
               + " y el segundo es: " + getDato2();
   }

   public E getDato1() {
       return dato1;
   }

   public void setDato1(E dato1) {
       this.dato1 = dato1;
   }

   public E getDato2() {
       return dato2;
   }
   public void setDato2(E dato2) {
       this.dato2 = dato2;
   }
}



package miselania.tiposgenericos;

/**
*
* @author hernan
*/
public class PruebaTiposGenericos {
   public static void main(String[] args) {
       Concatenador concatenador1 = new Concatenador();
       concatenador1.setDato1(42.35F);
       concatenador1.setDato2(2.365F);
       Concatenador concatenador2 = new Concatenador();
       concatenador2.setDato1("Hola");
       concatenador2.setDato2("Adiós");
       Concatenador concatenador3 = new Concatenador();
       concatenador3.setDato1(23);
       concatenador3.setDato2(246);
       System.out.print(concatenador1 + "\n" + concatenador2
               + "\n" + concatenador3);
   }
}

En el programa anterior podemos observar que Concatenar funciona para diferentes tipos de datos concretos como es un Float, String y Integer, cada uno de estos utilizan a "E".

2) Ejemplo de interfaz Comparable

La implementación significa que objetos de la clase Persona pueden ser comparados cuando estos pertenezcan a una colección de objetos, y se podrá obtener ordenamiento automático de los elementos de dicha colección.
Para hacer que los objetos de una clase sean comparables solo se debe implementar la Interfaz Comparable como se muestra en la implementación siguiente. Además el método abstracto implementado "compareTo" define que atributo toma en cuenta para la ordenación, en este caso se ha tomado el atributo nombre de persona, de esa misma manera se procede con los valores alfabéticos. Si los atributos fuesen números entonces se hace una diferencia. recuerda si el resultado es -1, 0 y 1 tiene significado. 
package miselania.tiposgenericos;

/**
*
* @author hernan
*/
public class Persona implements Comparable<Persona> {

   private String nombre;

   public Persona(String nombre) {
       super();
       this.nombre = nombre;
   }
   

   public String getNombre() {
       return nombre;
   }

   public void setNombre(String nombre) {
       this.nombre = nombre;
   }

   @Override
   public int compareTo(Persona o) {
       return nombre.compareTo(o.nombre);
   }
   @Overrideción
   public String toString()
   {
       return "Nombre:"+this.nombre;
   }
}

package miselania.tiposgenericos;

import java.util.Set;
import java.util.TreeSet;

/**
*
* @author hernan
*/
public class PruebaComparable {
   public static void main(String[] args)
   {
      Set <Persona> lista = new TreeSet<Persona>();
      lista.add(new Persona("Juan"));
      lista.add(new Persona("Alberto"));
      lista.add(new Persona("Ivan"));
      lista.add(new Persona("Angel"));
      
      System.out.println(lista);
   }
   
}
El resultado de la ejecución de la prueba es una lista de objetos ordenados alfabéticamente por el nombre, como se muestra enseguida.
run:
[Nombre:Alberto, Nombre:Angel, Nombre:Ivan, Nombre:Juan]
BUILD SUCCESSFUL (total time: 1 second)
 

IV) Tarea

1) Modificar el programa para ordenas los elementos en forma descendente utilizando la interface Comparable.

2) Utilizando la interfaz comparable y en base al programa de la práctica, hacer que los elementos se ordenen en base a la edad de una persona. Añadir el atributo edad a la clase Persona.

3) Implementar una lista enlazada utilizando tipos genéricos.

4) Utilizar la clase Persona que implementa la interface Comparable con una lista LinkedList como la colección de objetos.

5) Modificar la tarea 3 para que la lista enlazada de tipos genéricos implemente la Interface Iterator.

6) Utilice la interfaz Map para crear un diccionario de palabras.


V) Referencias

  • Beginning Java 2 JDK 1.5 Edition, Ivor Horton´s
Comments