You are on page 1of 7

5 cosas que usted no sabía sobre...

la
API Java Collections, Parte 1
Personalice y extienda Java Collections

Ted Neward
Publicado en 26-11-2012
FacebookTwitterLinked InGoogle+

La API Java Collections llegó a muchos desarrolladores Java como un muy


necesitado reemplazo del array Java estándar y todas sus falencias. La
asociación de Colecciones, principalmente con ArrayList no es un error, pero
hay mucho más que las Colecciones para aquellos que siguen buscando.
Acerca de esta serie
¿Así que usted considera que sabe acerca de programación Java? El hecho es
que la mayoría de los desarrolladores no profundizan en la plataforma Java, y
apenas aprenden lo necesario para realizar su trabajo. En esta serie, Ted
Neward profundiza hacia el núcleo de la funcionalidad de la plataforma Java
para descubrir pequeños datos conocidos que pueden ayudarle a resolver
incluso los desafíos de programación más complicados.
De forma similar, aunque Map (y su implementación frecuentemente
seleccionada, HashMap) son excelentes para realizar pares nombre-valor o
clave-valor, no existe razón para que usted se limite a estas herramientas ya
conocidas. Es posible reparar gran cantidad de código propenso a errores con
la API adecuada, o incluso don la Colección adecuada.
Este segundo artículo de la serie 5 cosas es el primero de varios dedicados a
Colecciones, dado que son un elemento bastante central de lo que hacemos en
la programación Java. Comenzaré con una mirada a las formas más rápidas
(aunque posiblemente no las más comunes) de hacer las cosas de todos los
días, como intercambiar Array por List. Después de ello, ahondaremos en
información menos conocida, como la escritura de una clase personalizada de
Colecciones y la extensión de la API Java Collections.
1. Las Colecciones se imponen sobre los arreglos
Desarrolle habilidades de este tema
Este contenido es parte de un knowledge path progresivo para avanzar en sus
habilidades. Vea Conviértase en desarrollador Java
Los desarrolladores principiantes en tecnología Java pueden no saber que los
arreglos se incluyeron originalmente en el lenguaje para refutar las críticas al
desempeño de los desarrolladores C++ a comienzos de los años 90. Bien,
hemos recorrido un largo camino desde entonces, y las ventajas en
desempeño de los arrays generalmente se quedan cortas cuando se comparan
con las de las bibliotecas de Java Collections.
Volcar el contenido de arry en una cadena de caracteres, por ejemplo, requiere
de la iteración de todo el array y de la concatenación del contenido en
una String; mientras que, las implementaciones de Colecciones tienen todas
una implementación toString() viable.
Con excepción de casos raros, es una buena práctica convertir cualquier array
que llegue a usted en una colección, tan rápido como le sea posible. Esto
formula entonces la pregunta, ¿cuál es la forma más fácil para esta
conversión? Resulta ser que la API Java Collections lo facilita, como se
muestra en el Listado 1:
Listado 1. ArrayToList
1
2 import java.util.*;
3
4 public class ArrayToList
{
5
public static void main(String[] args)
6 {
7 // This gives us nothing good
8 System.out.println(args);
9
10 // Convert args to a List of String
List<String> argList = Arrays.asList(args);
11
12
// Print them out
13 System.out.println(argList);
14 }
15 }
16
Note que la List retornada no se puede modificar, por lo que los intentos de
añadir nuevos elementos a ella arrojarán una UnsupportedOperationException.
Y, dado que Arrays.asList() usa un parámetro varargs para los elementos a
añadir a la List, también es posible crearlo para crear fácilmente Listas fuera
de objetos new.
2. La iteración es ineficiente
No es poco común desear mover el contenido de una colección
(particularmente una que haya sido elaborada a partir de un array) hacia otra
colección, o remover una colección pequeña de objetos de una más grande.
Es posible verse tentado(a) a simplemente iterar la colección y a añadir o
remover cada elemento a medida que se encuentra, pero no lo haga.
Iterar, en este caso, tiene grandes desventajas:
 Puede ser ineficiente cambiar el tamaño de la colección con cada adición o
eliminación.
 Existe una pesadilla potencial por concurrencia en adquirir un bloqueo, efectuar
la operación y liberar el bloqueo cada vez.
 Existe la condición de actualización causada por otros hilos que golpean su
colección mientras se lleva a cabo la adición o eliminación.
Es posible evitar estos problemas utilizando addAll oremoveAll para pasar la
colección que contiene los elementos que usted desea añadir o remover.
3. Para hacer un bucle en cualquier Iterable
La mejora para bucle, una de las mayores mejoras añadidas al lenguaje Java
en Java 5, eliminó la última barrera para trabajar con Java Collections.
Antes, los desarrolladores tenían que obtener manualmente un Iterator,
usar next() para obtener el objeto al que se apuntaba desde el Iterator y
verificar si había más objetos disponibles usando hasNext(). Después de Java
5, tenemos la libertad de usar una variante for-loop que maneja todo lo anterior
de forma silenciosa.
En realidad, esta mejora funciona con cualquier objeto que implemente la
interfaz Iterable , no solo Collections.
El Listado 2 muestra un enfoque para crear una lista de hijos de un
objeto Person disponible, como en unIterator. En lugar de manejar una
referencia hacia la List interna (lo cual permitiría a los interlocutores externos
a Person añadir niños a su familia — algo que a muchos padres no les
agradaría), el tipo Person implementa Iterable. Este enfoque también hace
posible que el bucle mejorado pase por cada uno de los hijos.
Listado 2. Mejora de bucle: Muéstreme sus hijos
1 // Person.java
2 import java.util.*;
3
4 public class Person
5 implements Iterable<Person>
{
6 public Person(String fn, String ln, int a, Person... kids)
7 {
8 this.firstName = fn; this.lastName = ln; this.age = a;
9 for (Person child : kids)
10 children.add(child);
}
11 public String getFirstName() { return this.firstName; }
12 public String getLastName() { return this.lastName; }
13 public int getAge() { return this.age; }
14
15 public Iterator<Person> iterator() { return children.iterator(); }
16
17 public void setFirstName(String value) { this.firstName = value; }
public void setLastName(String value) { this.lastName = value; }
18 public void setAge(int value) { this.age = value; }
19
20 public String toString() {
21 return "[Person: " +
22 "firstName=" + firstName + " " +
23 "lastName=" + lastName + " " +
"age=" + age + "]";
24 }
25
26 private String firstName;
27 private String lastName;
28 private int age;
29 private List<Person> children = new ArrayList<Person>();
}
30
31 // App.java
32 public class App
33 {
34 public static void main(String[] args)
35 {
36 Person ted = new Person("Ted", "Neward", 39,
37 new Person("Michael", "Neward", 16),
new Person("Matthew", "Neward", 10));
38
39 // Iterate over the kids
40 for (Person kid : ted)
41 {
42 System.out.println(kid.getFirstName());
43 }
}
44 }
45
46
47
48
49
50
51
Utilizar Iterable tiene algunas desventajas obvias cuando se hace modelaje
de dominio, dado que solo una colección de objetos puede ser soportada tan
"implícitamente" mediante el método iterator() . Sin embargo, para casos en
los que la colección de hijos es obvia, Iterable hace que la programación
contra el tipo de dominio sea mucho más fácil y más obvia.
4. Algoritmos clásicos y personalizados
¿Alguna vez ha deseado recorrer una Collection, pero en reversa? Allí es
donde un algo ritmo clásico de Java Collections es útil.
Los hijos de Person en elListado 2 de arriba están listados en el orden en que
se recorrieron; pero, ahora usted desea listarlos en el orden invertido. Si bien
es posible escribir otro bucle para insertar cada objeto en una
nueva ArrayList en el orden opuesto, la codificación podría crecer
tediosamente después de la tercera o cuarta vez.
Es aquí donde el algoritmo poco utilizado del Listado 3 entra en escena:
Listado 3. ReverseIterator
1
2 public class ReverseIterator
3 {
4 public static void main(String[] args)
{
5
Person ted = new Person("Ted", "Neward", 39,
6 new Person("Michael", "Neward", 16),
7 new Person("Matthew", "Neward", 10));
8
9 // Make a copy of the List
10 List<Person> kids = new ArrayList<Person>(ted.getChildren());
// Reverse it
11
Collections.reverse(kids);
12 // Display it
13 System.out.println(kids);
14 }
15 }
16
La clase Collections tiene cierto número de estos "algoritmos", métodos
estáticos que se implementan para tomarCollections como parámetros y
proporcionar comportamientos independientes de la implementación sobre la
colección como un todo.
Y aún más, los algoritmos presentes en la clase Collections ciertamente no
son la última palabra en un gran diseño de API — por ejemplo, prefiero
métodos que no modifiquen el contenido (de la Collection ingresada)
directamente. Así que es bueno que sea posible escribir algoritmos
personalizados usted mismo(a), como el que se muestra en el Listado 4:
Listado 4. ReverseIterator simplificado
1
class MyCollections
2
{
3 public static <T> List<T> reverse(List<T> src)
4 {
5 List<T> results = new ArrayList<T>(src);
6 Collections.reverse(results);
return results;
7
}
8 }
9
5. Extienda la API Collections
El algoritmo personalizado de arriba muestra un punto final sobre la API Java
Collections: que siempre se pretendió para que se extendiera y modificara para
adaptarse a los propósitos específicos de los desarrolladores.
Así, por ejemplo, digamos que usted hubo necesidad de la lista de hijos en la
clase Person siempre se ordenara por edad. Aunque usted pudo haber escrito
código para ordenar los hijos una y otra vez (usando el
método Collections.sort , tal vez), habría sido mucho mejor tener una
clase Collectionque los ordenara por usted.
De hecho, es posible que usted ni siquiera se haya preocupado por preservar
el orden en que los objetos se insertaban en Collection (lo cual es la principal
justificación para una List). Tal vez usted solo desea mantenerlos en un orden
clasificado.
Ninguna claseCollection dentro de java.util cumple estos requisitos, pero
escribir una es trivial. Todo lo que es necesario hacer es crear una interfaz que
describa el comportamiento abstracto que la Collection debería proporcionar.
En el caso de una SortedCollection, la intención es completamente
conductual.
Listado 5. SortedCollection
1 public interface SortedCollection<E> extends Collection<E>
2 {
3 public Comparator<E> getComparator();
4 public void setComparator(Comparator<E> comp);
}
5
Es casi decepcionante escribir una implementación de esta nueva interfaz:
Listado 6. ArraySortedCollection
1 import java.util.*;
2
3 public class ArraySortedCollection<E>
implements SortedCollection<E>, Iterable<E>
4 {
5 private Comparator<E> comparator;
6 private ArrayList<E> list;
7
8 public ArraySortedCollection(Comparator<E> c)
9 {
this.list = new ArrayList<E>();
10 this.comparator = c;
11 }
12 public ArraySortedCollection(Collection<? extends E> src, Comparator<E> c)
13 {
14 this.list = new ArrayList<E>(src);
this.comparator = c;
15 sortThis();
16 }
17
18 public Comparator<E> getComparator() { return comparator; }
19 public void setComparator(Comparator<E> cmp) { comparator = cmp; sortThis();
20
21 public boolean add(E e)
{ boolean r = list.add(e); sortThis(); return r; }
22 public boolean addAll(Collection<? extends E> ec)
23 { boolean r = list.addAll(ec); sortThis(); return r; }
24 public boolean remove(Object o)
25 { boolean r = list.remove(o); sortThis(); return r; }
26 public boolean removeAll(Collection<?> c)
{ boolean r = list.removeAll(c); sortThis(); return r; }
27 public boolean retainAll(Collection<?> ec)
28 { boolean r = list.retainAll(ec); sortThis(); return r; }
29
30 public void clear() { list.clear(); }
31 public boolean contains(Object o) { return list.contains(o); }
32 public boolean containsAll(Collection <?> c) { return list.containsAll(c); }
public boolean isEmpty() { return list.isEmpty(); }
33 public Iterator<E> iterator() { return list.iterator(); }
34 public int size() { return list.size(); }
35 public Object[] toArray() { return list.toArray(); }
36 public <T> T[] toArray(T[] a) { return list.toArray(a); }
37
38 public boolean equals(Object o)
{
39 if (o == this)
40 return true;
41
42 if (o instanceof ArraySortedCollection)
43 {
44 ArraySortedCollection<E> rhs = (ArraySortedCollection<E>)o;
return this.list.equals(rhs.list);
45 }
46
47 return false;
48 }
49 public int hashCode()
50 {
return list.hashCode();
51 }
52 public String toString()
53 {
54 return list.toString();
55 }
56
private void sortThis()
57 {
58 Collections.sort(list, comparator);
59 }
60 }
61
62
63
64
65
66
67
68
69
70
Esta implementación 'rápida y poco decente', escrita sin optimizaciones en
mente, obviamente soportaría algo de trabajo adicional. Pero el punto es que la
API Java Collections nunca pretendió dar la última palabra sobre todas las
cosas relacionadas con colecciones. Esta necesita y fomenta las extensiones.
Ciertamente, algunas extensiones serán de la variedad "trabajo pesado", como
las introducidas en java.util.concurrent. Pero otras serán tan simples como
escribir un algoritmo personalizado o una extensión simple hacia una
clase Collection existente.
Extender la API Java Collections puede parecer algo abrumador, pero una vez
que comience a hacerlo encontrará que no es tan difícil como usted pensó.
En conclusión
Así como Java Serialization, la API Java Collections contiene bastantes
rincones y ranuras sin explorar — que es por lo cual no hemos terminado con
este tema. El siguiente artículo de la serie 5 cosas le proporcionará cinco
maneras adicionales para hacer mucho más con la API Java Collections.

You might also like