Trabajando con colecciones

Anuncio
Trabajando con
colecciones
Autor: Ing. Mariela Gutierrez
Trabajando con colecciones
Trabajando con colecciones
Una colección es un objeto que representa un conjunto de objetos. Las colecciones nos
permiten modelar relaciones uno a muchos (1..n).
La jerarquía de colecciones
La interface Collection
Tiene métodos comunes a todas las colecciones que nos permiten:

Agregar un elemento

Quitar un elemento

Averiguar si un elemento está contenido en la colección

Averiguar la cantidad de elementos de la colección

etc.
Algunas observaciones sobre las colecciones:
1. Collection es una interface, no una clase, por lo tanto no puede instanciarse.
Trabajando con colecciones
2. Todos los métodos de Collection reciben por parámetro y retornan Object, por lo
que al sacar un elemento de la colección será necesario castearlo para trabajar con
él.
3. Usar Collection no garantiza el orden.
4. Para recorrer una colección será necesario utilizar su iterator. Los iterators reciben
los mensajes:
boolean hasNext(). Que retorna true si existe el siguiente elemento.
Object next(). Retorna el siguiente elemento
Iterator it = this.getEmpleados().iterator();
while ( it.hasNext() ) {
Empleado empl = (Empleado) it.next();
System.out.println( epl.getNombre() );
}
Otras colecciones
Interface List
Una lista es una colección indexada por un número que representa la posición. Trabaja
como una pila: se agregan elementos al final y se elimina siempre el primer elemento.
Interface Set
Un set Collection sin elementos repetidos implementada por HashSet y TreeSet. Set no
agrega nada a la interface Collection, pero el contrato se modifica (me asegura que mi
colección no va a tener elementos duplicados).
Igualdad / Identidad
Las implementaciones de Collection necesitan poder comparar dos elementos por
igualdad, por ejemplo, para saber si un elemento está en una colección (método contains).
Las implementaciones de Set validan duplicados basándose en la igualdad de sus
elementos.
Dos objetos son idénticos cuando son el mismo objeto. Si dos variables a y b referencian
al mismo objeto entonces a == b se cumple.
Por defecto, dos objetos son iguales cuando son el mismo objeto, pero nosotros podemos
cambiar la implementación del método equals para ciertos objetos del dominio, de manera que
Trabajando con colecciones
si tengo dos personas con el mismo número de documento, quizás me interese tratarlas como
personas “iguales”:
public boolean equals(Object objeto) {
if(! objeto instanceof Persona) {
return false;
}
Persona persona = (Persona) objeto;
return
this.getDocumento().equals(persona.getDocumento());
}
De la misma manera conviene redefinir los métodos:
toString( ) para visualizar información representativa de un objeto, y
hashCode( ) un número (clave hash) que devuelve cada objeto, que sigue las siguientes
reglas:

si una clase redefine equals, también debe redefinir el hashCode;

si dos objetos son iguales, deben devolver el mismo hashCode, por lo que el
hashCode no sirve para identificar unívocamente un objeto:

si un campo no se usa para comparar igualdad, tampoco debe ser usado en el
método hashCode.
Un ejemplo de redefinición de hashCode:
//Clase Persona
public Documento getDocumento(){
return this.documento;
}
public int hashCode(){
return this.getDocumento().hashCode();
}
Si el equals se define en base al documento, es importante que el hashCode también.
Comparando objetos
La interfaz Comparable nos permite relacionar objetos para poder darles un ordenamiento
natural. Si por ejemplo decimos que las personas implementan la interfaz Comparable esto
Trabajando con colecciones
significa que debemos implementar el método compareTo. El contrato de este método nos dice
que debe devolver:

un número negativo si el objeto receptor es menor que el objeto recibido como
argumento

un número positivo si el objeto receptor es mayor que el objeto recibido como
argumento, o

cero si ambos son iguales.
Para comparar dos personas por edad:
public int compareTo(Object objeto){
Persona persona = (Persona) objeto;
this.compararPorEdad(persona);
}
private int compararPorEdad(Persona persona) {
if ( persona.getEdad() == this.getEdad() )
return 0;
if( persona.getEdad() > this.getEdad() ) {
return -1;
} else {
return 1;
}
}
public int getEdad(){
return this.edad;
}
El mayor problema de esta forma de comparación es que nos permite una única forma de
comparar personas. ¿Y si ahora necesito comparar las personas en forma alfabética por
apellido?
La interface Comparator me permite definir más formas de ordenamiento.
public class ComparadorPersonasPorEdad implements
Comparator {
public int compare(Object obj1, Object obj2) {
per1 = (Persona) obj1;
Trabajando con colecciones
per2 = (Persona) obj2;
if( per2.getEdad() == per1.getEdad() )
return 0;
if( per2.getEdad() > per1.getEdad() ) {
return -1;
} else {
return 1;
}
}
}
public class ComparadorPersonasPorNombre implements
Comparator {
public int compare(Object obj1, Object obj2) {
per1 = (Persona) obj1;
per2 = (Persona) obj2;
return per1.getApellido().compareTo(per2.
getApellido());
}
}
Ordenando colecciones
En Java, la mayoría de las utilidades básicas ya están incluidas en la máquina virtual.
Existen clases que agrupan varias operaciones útiles de un tópico particular. En este caso
vamos a trabajar con la clase java.util.Collections.
Esta clase contiene muchos métodos de clase o estáticos, para trabajar con colecciones.
Por supuesto esta clase tiene un método para ordenar colecciones de elementos, y es eso lo
que nos ocupa ahora.
static void sort(List list)
Según la documentación:
Sorts the specified list into ascending order, according to the natural ordering of its
elements.
Este
método
puede
ordenar
cualquier
colección
que
implemente
la
interface
java.util.List, y ese orden natural se refiere al orden que indica el método compareTo de
la interface Comparable.
Trabajando con colecciones
Pero, ¿qué hacemos si queremos ordenar las personas también por orden alfabético?
En la clase Collections existe el método:
static void sort(List list, Comparator c)
Sorts the specified list according to the order induced by the specified comparator.
¿Qué es el segundo parámetro del método? java.util.Comparator es otra interface que se
usa para comparar objetos, y que pide a las clases que la implementan los siguientes métodos:
int compare(Object o1, Object o2)
Compares its two arguments for order.
boolean equals(Object obj)
Indicates whether some other object is "equal to" this Comparator.
El método equals( Object obj ) no nos interesa, es el que como en la clase
java.lang.Object determina si dos objetos son el mismo o no, el que nos interesa es el
otro, compare(Object o1, Object o1).
El funcionamiento es el mismo que el visto anteriormente en el interface comparable, solo
que en esta ocasión se pasan los dos objetos a comparar como argumentos. El resultado será:
negativo si o1 < o2
cero si o1 = o2
positivo si o1 > o2
Podríamos hacer que nuestras clases implementasen java.util.Comparator, pero
entonces no habríamos avanzado mucho desde el ejemplo anterior, es decir, estaríamos
limitados a un orden, por lo que lo normal será escribir distintas clases que implementen este
interface para después usar la que necesitemos en cada momento.
Para ordenar nuestra clase Persona escribiremos dos clases, una para ordenarlos por
apellido, y otra por edad.
import java.util.Comparator;
Trabajando con colecciones
class NombreComparator implements Comparator {
public int compare(Object o1, Object o2) {
Persona per1 = (Persona) o1;
Persona per2 = (Persona) o2;
return
per1.getNombre().compareTo(per2.getNombre());
}
public boolean equals(Object o) {
return this == o;
}
}
import java.util.Comparator;
class EdadComparator implements Comparator {
public int compare(Object o1, Object o2) {
Persona per1 = (Persona) o1;
Persona per2 = (Persona) o2;
return per1.getEdad() - per2.getEdad();
}
public boolean equals(Object o) {
return this == o;
}
}
Ahora sólo nos queda usarlos:
Collections.sort(lista, new NombreComparator());
Collections.sort(lista, new EdadComparator());
Otros métodos útiles de la clase Java.util.Collections
static Object max(Collection coll)
Returns the maximum element of the given collection, according to the natural
ordering of its elements.
Trabajando con colecciones
static Object max(Collection coll, Comparator comp)
Returns the maximum element of the given collection, according to the order
induced by the specified comparator.
static Object min(Collection coll)
Returns the minimum element of the given collection, according to the natural
ordering of its elements.
static Object min(Collection coll, Comparator comp)
Returns the minimum element of the given collection, according to the order induced
by the specified comparator.
Para mayor información sobre estos y otros métodos de la clase Collections, ver
http://download.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html.
La biblioteca Commons-Collections
¿Qué es una biblioteca o library?
En la mayoría de los sistemas operativos actuales, se ofrece una cantidad de código para
simplificar la tarea de programación. Este código toma la forma, normalmente, de un conjunto
de bibliotecas dinámicas que las aplicaciones pueden llamar cuando lo necesiten. Pero la
Plataforma Java está pensada para ser independiente del sistema operativo subyacente, por lo
que las aplicaciones no pueden apoyarse en funciones dependientes de cada sistema en
concreto. Lo que hace la Plataforma Java, es ofrecer un conjunto de bibliotecas estándar, que
contiene mucha de las funciones reutilizables disponibles en los sistemas operativos actuales.
Las bibliotecas de Java tienen tres propósitos dentro de la Plataforma Java.:
1. Al igual que otras bibliotecas estándar, ofrecen al programador un conjunto bien
definido de funciones para realizar tareas comunes, como manejar listas de
elementos u operar de forma sofisticada sobre cadenas de caracteres (Strings).
2. Además, las bibliotecas proporcionan una interfaz abstracta para tareas que son
altamente dependientes del hardware de la plataforma destino y de su sistema
operativo. Tareas tales como manejo de las funciones de red o acceso a archivos,
suelen depender fuertemente de la funcionalidad nativa de la plataforma destino.
En el caso concreto anterior, las bibliotecas java.net y java.io implementan el
código nativo internamente, y ofrecen una interfaz estándar para que aplicaciones
Java puedan ejecutar tales funciones.
Trabajando con colecciones
3. Finalmente, no todas las plataformas soportan todas las funciones que una
aplicación Java espera. En estos casos, las bibliotecas bien pueden emular esas
funciones usando lo que esté disponible, o bien ofrecer un mecanismo para
comprobar si una funcionalidad concreta está presente.
En general, las bibliotecas tienen la extensión .jar y se agregan al proyecto en el classpath.
En el lenguaje de programación Java se entiende por Classpath una opción admitida en la
línea de órdenes o mediante variable de entorno que indica a la Máquina Virtual de Java dónde
buscar paquetes y clases definidas por el usuario a la hora de ejecutar programas.
La biblioteca commons-collections pertenece al conjunto de bibliotecas del proyecto
Apache Software Foundation (http://commons.apache.org/).
El proyecto Commons de Apache Software Foundation
El objetivo de Commons es proporcionar componentes Java reutilizables y de código
abierto.
Es un lugar para la colaboración y participación, donde los desarrolladores de la comunidad
Apache trabajan juntos en proyectos que serán utilizados por otros proyectos de Apache y por
los usuarios de Apache. Los desarrolladores harán un esfuerzo para asegurar que sus
componentes tienen un mínimo de dependencias en otras bibliotecas de software , por lo que
estos componentes se pueden implementar fácilmente. Además, los componentes mantendrán
sus interfaces lo más estable posible, para que los usuarios de Apache, así como otros
proyectos de Apache, puede aplicar estos componentes sin tener que preocuparse por los
cambios en el futuro.
La
biblioteca
commons-collections
puede
descargarse
desde
http://commons.apache.org/collections/download_collections.cgi.
Algunos métodos útiles de Commons-Collections
Los métodos que nos interesan en este momento se encuentran en la clase
CollectionUtils:
Método Collect
static Collection collect(Collection inputCollection,
Transformer transformer)
Este método retorna una colección donde cada elemento es el elemento correspondiente
en la colección parámetro transformado por el Transformer dado.
Trabajando con colecciones
¿Qué es un Transformer?
Transformer es una interface cuyo contrato tiene un único método:
java.lang.Object
transform(java.lang.Object input), que transforma un
objeto.
Un ejemplo, aumentar un 20% el sueldo a todos los empleados de la compañía:
Como Transformer es una interfaz, podemos implementarla a través de una clase anónima
(ya incluida en la biblioteca, definiendo el método transform() que forma parte del contrato.
La conveniencia de utilizar una clase anónima se da cuando quiero definir un comportamiento
muy específico que no amerita tener un class porque ese comportamiento sólo tiene sentido en
ese contexto.
public void aumentarSueldo() {
Collection nuevaColeccionDeEmpleados =
CollectionUtils.collect(this.getEmpleados(), new Transformer() {
public Object transform(Object arg0) {
Empleado e = (Empleado)arg0; //Casteo al Object
a Empleado
e.setSueldo( e.getSueldo() * 1.2f); //Sumo un
20% al sueldo de todos los empleados
return e;
}
});
this.setEmpleados(nuevaColeccionDeEmpleados);
}
Existe un método que tiene la misma funcionalidad pero modificando la misma colección:
static void transform(java.util.Collection collection,
Transformer transformer)
public void aumentarSueldo() {
CollectionUtils.collect(this.getEmpleados(), new
Transformer() {
public Object transform(Object arg0) {
Empleado e = (Empleado)arg0; //Casteo al
Object a Empleado
e.setSueldo( e.getSueldo() * 1.2f);
//Sumo un 20% al sueldo de todos los empleados
return e;
Trabajando con colecciones
}
});
}
Método Select
static Collection select(Collection inputCollection,
Predicate predicate)
Este método devuelve una nueva colección sólo con los elementos que cumplen la
condición dada por el Predicate indicado.
¿Qué es un Predicate?
Es una interface cuyo contrato es el siguiente:
boolean evaluate(java.lang.Object object)
Un ejemplo es seleccionar aquellos empleados que cobran un sueldo mayor que $8000.
public void empleadosConMejorSueldo() {
Collection mejoresPagados =
CollectionUtils.select(this.getEmpleados(), new Predicate() {
public boolean evaluate(Object arg0) {
Empleado e = (Empleado)arg0; //Casteo al
Object a Empleado
return e.getSueldo() > 8000; //Evalúo si
es mayor a 8000
}
});
}
Existe un método posee la misma funcionalidad pero modificando la misma colección:
static void filter(java.util.Collection collection,
Predicate predicate)
Método forAllDo
static void forAllDo(java.util.Collection collection,
Closure closure)
Ejecuta el Closure dado a cada elemento de la colección.
¿Qué es un Closure?
Es una interface cuyo contrato es el siguiente:
Trabajando con colecciones
void execute(java.lang.Object input)
Por ejemplo, imprimir la nómina de empleados:
public void nominaEmpleados() {
CollectionUtils.select(this.getEmpleados(), new Predicate()
{
public void execute(Object arg0) {
Empleado e = (Empleado)arg0; //Casteo al
Object a Empleado
System.out.println( e.getNombre( ) );
//Imprimo el nombre del empleado
}
});
}
Para mayor información sobre los métodos de la clase CollectionUtils y de otras clases de
la biblioteca commons-collections, ver
http://commons.apache.org/collections/apidocs/index.html.
Descargar