Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Genéricos y Colecciones

A continuación dejo el resumen del séptimo capítulo (Generics and Collections) del libro "Sun Certified Programmer for Java 6 Study Guide".

Sobreescribiendo hashCode() y equals() (Objetivo 6.2):
Métodos de la clase Object que se necesitan saber para el examen:
  • boolean equals(Object obj)
  • void finalize()
  • int hashCode()
  • final void notify()
  • final void notifyAll()
  • final void wait()
  • String toString()
Método equals()
El operador == permite evaluar si dos referencias son iguales, es decir, cuando se refieren al mismo objeto. Para comparar si dos objetos son iguales, si los objetos en sí mismo son idénticos, se utiliza el método equals().
La clase String y todas las clases envoltorios (wrappers) de tipos primitivos, tienen el método equals() redefinido. Por esto mismo se pueden comparar si dos objetos tienen un mismo estado.
Si se quiere utilizar (de manera correcta) un objeto como clave en una colección hash, se debe sobreescribir el método equals(), para de este modo permitir considerar dos instancias iguales.

Método hashCode()
Generalmente los hashcodes son utilizados para mejorar la performance de grandes colecciones de información. Este código se utiliza para determinar donde se guardará el objeto dentro de una colección (HashMap, HashSet o sus subtipos), y como se debe localizar. Para el examen se debe saber que colecciones lo utilizan, y diferenciar los usos apropiados o correctos de los legales.

Colecciones (Objetivo 6.1):
Tipos de colecciones:
  • List: Sus operaciones están basadas en los índices de los elementos en la colección. Todas sus implementaciones tienen sus índices ordenados. Cuando se agrega un elemento a la colección se puede hacer especificando el índice en el cual tiene que estar, o sin especificarlo (en este caso se posiciona al final).
    - ArrayList: Provee una rápida iteración y un rápido acceso aleatorio. Pero es más lento para insertar y quitar elementos que LinkedList.
    - Vector: Es una colección primitiva de las primeras versiones de java. Es básicamente similar a ArrayList, salvo que todos sus métodos están sincronizados para el manejo seguro de hilos (thread safe).
    - LinkedList: Es una colección como ArrayList, excepto porque sus elementos se encuentran doblemente ligados (uno con otro). Este doble vinculo otorga nuevos métodos para agregar o quitar elementos del principio o el final. Su iteración es más lenta que ArrayList. A partir de Java 5, esta colección implementa la interface Queue. De esta manera puede ser tratada como una cola, y posee los métodos: peek(), poll() y offer().
  • Set: Es un conjunto de elementos únicos. Es decir, no se permiten objetos duplicados. Para lograr este comportamiento se utiliza el método equals(), que permite verificar si dos instancias son idénticas.
    - HashSet: Es una colección que no mantiene un orden específico. Utiliza el código de hash de los objetos insertados. Por este motivo mientras más eficiente sea la implementación del método hashCode(), mejor será la performance de acceso. Es útil cuando se necesita una colección sin elementos duplicados y en la cual no importe el orden de iteración.
    - LinkedHashSet: Es la versión ordenada de HashSet. Mantiene un doble enlace entre sus elementos como si fuera una lista. Es útil cuando se necesita (a diferencia de HashSet) una colección en donde el orden de iteración sea importante.
    - TreeSet: Es una colección que contiene sus elementos ordenados. Utiliza un árbol de estructura rojo-negro, y garantiza que los elementos se encuentren en orden ascendente, de acuerdo a su orden natural. A partir de Java 6, esta colección implementa NavigableSet.
  • Map: Son colecciones con elementos compuestos de parejas clave/valor. Las claves de los elementos son únicas. Al igual que la interface Set, utiliza el método equals() para determinar si dos objetos son iguales. Muchas de sus operaciones están basadas en las claves.
    - HashMap: Es una colección que no mantiene un orden específico. Utiliza el código de hash sobre las claves, para determinar el orden de sus elementos. Es útil cuando se necesita un mapa en el cuál no importe el orden de iteración. Permite un solo valor null como clave, y multiples valores null en la colección.
    - Hastable: Como la clase Vector, es una implementación de las primeras versiones de java. Es similar a HashMap, a diferencia que sus métodos se encuentran sincronizados. Otra de las diferencias es que no permite valores null tanto en la claves como en los valores.
    - LinkedHashMap: Similar a HashMap, a diferencia que mantiene los elementos ordenados. Y porque es un poco más lento para agregar o quitar elementos de la colección. Pero permite una iteración más veloz.
    - TreeMap: Es un mapa ordenado de forma natural. También permite definir un tipo de orden (a través de las interfaces Comparable y Comparator). A partir de Java 6, implementa la interface NavigableMap.
  • Queue: Es una colección de tipo “cola”, la cual permite almacenar cosas que deben ser procesadas de cierta manera. Generalmente funcionan como colas FIFO (First-In, First-Out).
    - PriorityQueue: Es una clase nueva de Java 5. Generalmente se utiliza la clase LinkedList para implementar colas. Pero el propósito de esta colección, es crear colas con prioridades, a diferencia de las colas FIFO. Sus elementos se encuentran ordenados por orden natural, o de acuerdo a un objeto Comparator. El orden de los elementos representa la prioridad.
Ordenando colecciones
Las clases Arrays y Collections poseen un método para ordenar sus Elementos. Arrays.sort() y Collections.sort(). La única condición que deben cumplir los elementos del arreglo, es que sean del mismo tipo. Objetos de diferentes tipos no son mutuamente comparables.

java.lang.Comparable
Para que una colección de objetos pueda ser ordenada, los mismos deben implementar las interface Comparable. Dicha interface solo posee el método compareTo().
thisObject.compareTo(anotherObject);
Este método devuelve un valor entero con las siguientes características:
  • Negativo: thisObject < anotherObject
  • Cero: thisObject == anotherObject
  • Positivo: thisObject > anotherObject
La interface Comparable (a partir de Java 5) permite utilizar un tipo genérico. De esta manera no se deben realizar casteos en ninguna parte del código. Cada clase que implemente la interface, definirá el tipo de objeto con el que trabajara.
public int compareTo(T o);

java.util.Comparator
Existe una versión sobrecargada del método Collections.sort(), la cual permite pasar como argumento un objeto Comparator. La interface Comparator brinda la capacidad de ordenar una colección de diferentes maneras. Para utilizar esta interface, se debe declarar una nueva clase que la implemente, y sobrescriba el método compare(). Al igual que Comparable puede utilizar un tipo parametrizado (lo cual no es obligatorio, pero generará advertencias al momento de la compilación).
public int compare(T o1, T o2);

Arrays.sort()
La clase Arrays (al igual que Collections) posee dos versiones del método sort():
• Arrays.sort(arrayToSort): Para cualquier tipo de objeto o tipos primitivos.
• Arrays.sort(arrayToSort, Comparator): se puede utilizar solo para objetos (NO primitivos).

Buscando elementos en las colecciones
Las clases Collections y Arrays poseen un método que permite buscar un elemento específico, binarySearch(). Las búsquedas exitosas devuelven el valor entero del índice del elemento, las que no tienen éxito devuelven un entero negativo que representa el lugar de inserción. Es obligatorio que la colección se encuentre ordenada antes de realizar cualquier búsqueda, caso contrario se devolverá un valor impredecible. Si la colección ha sido ordenada mediante un objeto Comparator, la búsqueda debe realizarse pasando como segundo parámetro el mismo objeto Comparator.
Los siguientes métodos pertenecen tanto a Arrays como a Collections:
• binarySearch(ObjetoBuscado o)
• binarySearch(ObjetoBuscado o, Comparable c)

Convirtiendo arreglos a listas y listas a arreglos
Las interfaces List y Set poseen el método toArray(), el cual convierte la lista de elementos en un arreglo. La clase Arrays tiene un método llamado asList(), este copia el contenido de un arreglo adentro de una lista.
Cuando se actualiza un elemento de algunas de las dos colecciones, automáticamente se actualiza la otra.

Iterator
Es un objeto que está asociado con una colección específica. Permite iterar a través de la colección paso a paso. Los dos métodos de la interface Iterator que se deben saber para el examen son:
  • boolean hasNext(): Devuelve true si hay al menos un elemento más en la colección que se esta atravesando.
  • Object next(): Devuelve el próximo objeto en la colección, y mueve hacia adelante el puntero.
Se puede utilizar un tipo parametrizado junto al objeto Iterator, de esta menara el método next() devolverá dicho tipo, evitando las necesidades de casteo.

Set
Se debe prestar atención el tipo de implementación que se utiliza. Una colección HashSet permite contener elementos de diferentes tipos (si el tipo parametrizado es Object, o el mismo se excluye). Pero una colección TreeSet (la cual contiene a sus elementos en orden) no permite contener objetos que no sean comparables entre sí.

Map
A la hora de utilizar este tipo de colecciones, se debe prestar atención a las claves utilizadas. Es decir, si un objeto clave (insertado en la colección) luego modifica su estado, puede ser que el mismo no sea hallado en el mapa. Esta situación se da debido a que el método hashCode(), devuelve un código diferente.

NavigableSet
Esta interface (implementada únicamente por TreeSet) posee diversos métodos de utilidad:
  • lower(): Devuelve el elemento menor al elemento pasado como parámetro.
  • floor(): Devuelve el elemento menor o igual al elemento pasado como parámetro.
  • higher(): Devuelve el elemento mayor al elemento pasado como parámetro.
  • ceiling(): Devuelve el elemento mayor o igual al elemento pasado como parámetro.
  • pollFirst(): Devuelve el primer elemento y lo elimina de la colección.
  • pollLast(): Devuelve el último elemento y lo elimina de la colección.
  • descendingSet(): Devuelve una copia de la colección en orden inverso.
  • headSet(e): Devuelve un subconjunto que termina en el elemento (excluido) e.
  • tailSet(e): Devuelve un subconjunto que comienza en el elemento (incluido) e.
  • subSet(s, e): Devuelve un subconjunto que comienza en el elemento (incluido) s y finaliza en el elemento (excluido) e.
NavigableMap
Esta interface (implementada únicamente por TreeMap) posee diversos métodos de utilidad:
  • lowerKey(): Devuelve la clave menor a la clave pasada como parámetro.
  • floorKey(): Devuelve la clave menor o igual a la clave pasada como parámetro.
  • higherKey(): Devuelve la clave mayor a la clave pasada como parámetro.
  • ceilingKey(): Devuelve la clave mayor o igual a la clave pasada como parámetro.
  • pollFirstEntry(): Devuelve la primer pareja clave/valor y la elimina de la colección.
  • pollLastEntry(): Devuelve la ultima pareja clave/valor y la elimina de la colección.
  • descendigMap(): Devuelve una copia de la colección en orden inverso.
  • headMap(k): Devuelve un subconjunto que termina en la clave (excluida) k.
  • tailMap(k): Devuelve un subconjunto que comienza en la clave (incluida) k.
  • subMap(s, e): Devuelve un subconjunto que comienza en la clave (incluida) s y finaliza en la clave (excluida) e.
Queue
La colección PriorityQueue ordena sus elementos utilizando una prioridad definida por el usuario. La prioridad puede ser el orden natural, pero también se puede ordenar utilizando un objeto Comparator. La interface Queue posee métodos que no se encuentran en ninguna otra colección:
  • peek(): Devuelve el primer elemento de la cola (o el que tenga mayor prioridad).
  • poll(): Devuelve el primer elemento de la cola (o el que tenga mayor prioridad), y luego lo elimina de la cola.
  • offer(T o): Agrega un elemento al final de la cola (o en el lugar donde indique su prioridad).
RESUMEN: TWO-MINUTE DRILL
Sobrescribiendo hashCode() y equals() (Objetivo 6.2):
  • Los métodos equals(), hashCode() y toString() son públicos.
  • El método toString() permite mostrar el estado de un objeto mediante un String.
  • El operador == sirve para determinar si dos variables de referencia apuntan al mismo objeto.
  • El método equals() sirve para determinar si dos objetos poseen un estado equivalente.
  • Si no se sobrescribe el método equals(), los objetos no serán útiles como claves hash.
  • Si no se sobrescribe el método equals(), objetos diferentes no pueden ser considerados iguales.
  • La clase String y las clases envoltorios (wrappers) sobrescriben equals() y son claves hash eficientes.
  • Cuando se sobrescribe el método equals(), es conveniente utilizar el operador instanceof para estar seguro de que se está evaluando un objeto correcto.
  • Cuando se sobrescribe el método equals(), es conveniente comprar los atributos significantes del objeto.
  • Los características más importantes del método equals() son:
    - Es reflexivo: x.equals(x) es true.
    - Es simétrico: Si x.equals(y) es true, entonces y.equals(x) debe ser true.
    - Es transitivo: Si x.equals(y) es true, y y.equals(z) es true, entonces z.equals(x) es true.
    - Es consistente: Múltiples llamadas a x.equals(y) deben retornar el mismo resultado.
    - Null: Si x no es null, entonces x.equals(null) es false.
  • Si x.equals(y) es true, entonces x.hashCode() == y.hashCode() es true.
  • Si se sobrescribe equals(), es aconsejable sobrescribir hashCode().
  • HashMap, Hashtable, LinkedHashMap, HashSet y LinkedHashSet utilizan el código de hash.
  • Un hashCode() apropiado debe cumplir el contrato del método.
  • Un hashCode() eficiente debe retornar valores diferentes para distribuir a través de todas las claves.
  • El método equals() debe ser precioso como hashCode(), es decir, se deben utilizar las mismas variables de instancia para llevar a cabo sus operaciones.
  • Es legal que el método hashCode() devuelva el mismo valor para todas las instancias de una clase. Salvo que en la práctica esto no es eficiente.
  • Los características más importantes del método hashCode() son:
    - Es consistente: Múltiples llamados a x.hashCode() devuelven el mismo valor entero.
    - Si x.equals(y) es true, x.hashCode() == y.hashCode() es true.
    - Si x.equals(y) es false, entonces x.hashCode() == y.hashCode() puede ser tanto true como false, pero si es false la implementación tiende a tener mayor eficiencia.
  • Las variables de instancia transient son inapropiadas para utilizar en equals() y hashCode().
Colecciones (Objetivo 6.1):
  • Las acciones comunes sobre las colecciones son añadir elementos, eliminarlos, verificar su inclusión, obtenerlos y iterar sobre los mismos.
  • Existen cuatro tipos básicos de colecciones:
    - Lists
    - Sets
    - Maps
    - Queues
  • El ordenamiento puede ser alfabético, numérico o definido por el usuario.
Atributos clave de las clases de colección (Objetivo 6.1):
  • ArrayList: Rápida iteración y rápido acceso aleatorio.
  • Vector: Es como un ArrayList más lento, pero tiene sus métodos sincronizados.
  • LinkedList: Es bueno para agregar elementos al final, por ejemplo pilas y colas.
  • HashSet: Rápido acceso, asegura la no duplicación, no provee un orden.
  • LinkedHashSet: No posee elementos duplicados, itera por orden de inserción.
  • TreeSet: No posee elementos duplicados, itera en orden.
  • HashMap: Rápidas actualizaciones (clave/valor), permite una clave null y muchos valores null.
  • Hashtable: Es como un HashMap más lento, pero tiene sus métodos sincronizados. No permite valores null tanto en las claves como en los valores.
  • LinkedHashMap: Rápidas iteraciones, itera en orden de inserción o ultimo acceso. También permite valores null y una clave null.
  • TreeMap: Un mapa ordenado.
  • PriorityQueue: Una cola ordenada con las prioridades de sus elementos.
Utilizando las clases de colección (Objetivo 6.3):
  • Las colecciones pueden almacenar solo objetos, pero las primitivas pueden ser convertidas automáticamente a sus tipos envoltorios (autoboxed).
  • Las iteraciones se pueden realizar a través del operador for (nuevo) o con un objeto Iterator a través de los métodos hasNext() y next().
  • El método hasNext() determina si existen más elementos, el iterador no se mueve.
  • El método next() devuelve el siguiente elemento y mueve el iterador hacia adelante.
  • Para trabajar de manera eficiente, los objetos clave de un mapa deben sobrescribir el método equals() y hashCode().
  • Las colas utilizan el método offer() para añadir elementos, poll() para devolver y eliminar el elemento inicial, y peek() para obtener el elemento inicial.
  • A partir de Java 6 las clases TreeSet y TreeMap tienen nuevos métodos de navegación como floor() y higher().
  • Se pueden crear/extender sub-copias “respaldadas” de TreeSets y TreeMaps.
Ordenando y buscando en Arrays y Lists (Objetivo 6.5):
  • La ordenación se puede realizar en orden natural, o a través de un objeto Comparable o muchos Comparators.
  • Para implementar Comparable se debe utilizar el método compareTo(), el cual provee solo un modo de ordenación.
  • Se pueden crear muchos Comparators para ordenar una clase de diversas maneras, implementando el método compare().
  • Para que los elementos de una lista (List) puedan ser ordenados y buscados, los mismos deben ser comparables.
  • Para buscar elementos en un arreglo o lista, los elementos deben estar ordenados.
Clases de utilidad: Collections y Arrays (Objetivo 6.5):
  • Estas dos clases pertenecen al paquete java.util, y las mismas proveen:
    - Un método sort(). El cual ordena los elementos en orden natural o a través de un objeto Comparator.
    - Un método binarySearch(). Para buscar un elemento en un arreglo o lista previamente ordenado.
    - El método Arrays.asList crea una lista (List) a partir de un arreglo.
    - El método Collections.reverse() invierte el orden de los elementos de una lista.
    - El método Collections.reverseOrder() devuelve un Comparator que ordena inversamente.
    - Las interfaces List y Set poseen el método toArray() que convierte la colección en un arreglo.
Genéricos (Objetivo 6.4):
  • Los tipos genéricos permiten forzar la seguridad de los tipos, en tiempo de compilación, en las colecciones (o otras clases y métodos que utilicen tipos parametrizados).
  • Un ArrayList<T> permite almacenar objetos T o cualquier subtipo del mismo. (T también puede ser una interface).
  • Cuando se utilizan colecciones genéricas, no es necesario castear los objetos cuando se obtienen de la misma.
  • Se puede pasar una colección genérica como parámetro a un método que toma una colección no genérica, pero los resultados pueden ser inesperados.
  • Compilar sin ningún error no es lo mismo que compilar sin ninguna advertencia. Una advertencia de compilación no es considerado un error de compilación.
  • La información genérica de tipo no existe en tiempo de ejecución, es solo para la seguridad de tipos en tiempo de compilación. Mesclando genéricos con código legal se puede compilar correctamente, pero dicho código puede arrojar una excepción.
  • Las asignaciones polimórficas aplican solo al tipo base, no al parámetro de tipo genérico:
    - List<Animal> l = new ArrayList<Animal>(); // valido
    - List<Animal> l = new ArrayList<Dog>(); // inválido
  • La regla de asignación polimórfica aplica en cualquier lugar en donde se pueda realizar una asignación:
    - void foo(List<Animal> l) { } // no puede tomar un argumento List<Dog>
    - List<Animal> bar() { } // no puede devolver un List<Dog>
  • La sintaxis comodín permite a un método genérico, aceptar subtipos (o supertipos) de un tipo declarado como argumento de método: List<? extends Animal>
  • Cuando se utiliza un comodin <? extends Dog>, la colección puede ser accedida pero no modificada.
  • Cuando se utiliza el comodin List<?>, cualquier tipo genérico puede ser asignado a la referencia, pero solo para acceso, no para modificaciones.
  • List<?> es igual a List<? extends Object>.
  • Las convenciones de declaración utilizan la letra T para tipos y E para elementos de una colección.
  • Se puede utilizar más de un tipo parametrizado en una declaración.
  • Se puede declarar un tipo genérico utilizando un tipo que no esté definido en la clase: public <T> void makeList(T t) { }



This post first appeared on Sun Certified Java Programmer (SCJP), please read the originial post: here

Share the post

Genéricos y Colecciones

×

Subscribe to Sun Certified Java Programmer (scjp)

Get updates delivered right to your inbox!

Thank you for your subscription

×