Colecciones en Groovy

De ChuWiki

Veamos algunos ejemplos básicos de colecciones en Groovy

Tipos de colecciones[editar]

Las colecciones que podemos definir en groovy, aparte de sus posibles múltiples variantes e instancias concretas, son

// Array
Integer [] array = [1,2,3,4]     // No funciona igual con int[] array

// Listas
List list = [1,2,3,4]
def anotherList = [1,2,3,4]      // Por defecto, si no se especifica el tipo, es un ArrayList

// Conjuntos
Set set = [1,1,1,2,2,3,3]        // No contendrá elementos repetidos, es decir, valdrá [1,2,3]

// Map
Map map = ['one':1, 'two':2, 'three':3]    // Tres claves one,two,three, con una valor cada una 1,2,3

Los arrays tienen sus propios métodos, List y Set, al heredar de Collection tienen un conjunto similar de métodos (aunque cada uno tiene los suyos propios) y Map tiene su propio conjunto de métodos. Groovy se encarga de que los métodos sean parecidos en todos ellos, aunque no nos podemos fiar de que existan todos ellos en todos los tipos de colecciones, sobre todo operadores como +, <<, etc. Conviene siempre consultar la api de groovy. Los métodos de los arrays los podemos encontrar en el paquete primitive-types (aunque parezca que habla de int, boolean, float...., en realidad habla de arrays de int, boolean, float...). Los de List, Set, Collection y Map podemos encontrarlos en el paquete java.util.

Recorrer las colecciones[editar]

Independientemente de qué colección sea, se pueden recorrer de muchas formas. Dos de las más cómodas pueden ser con each(), o con un bucle for.

// Con each()
array.each{
    value-> println value
}
// Con un for
for (value in array){
    println value
}

each tiene una variante eachWithIndex() en la que además del valor, recibimos también el índice (la posición) del elemento dentro de la colección

// Con eachWithIndex()
List strings = ['hello','world']
strings.eachWithIndex {
    String entry, int i -> println "strings[$i] = $entry"}
}

Los Map son un poco distintos, al recorrerlos recibiremos un item que tiene dos atributos, clave y valor.

Map map = ['one':1, 'two':2, 'three':3]
map.each {
    item -> println "$item.key = $item.value"
}


Concatenar colecciones[editar]

Concatenar colecciones es fácil, basta sumarlas con el operador +

List strings = ['hello','world']
def moreStrings = ['more hello','more world']

List allStrings = strings + moreStrings
println "Sum of two lists ${allStrings})"

Un par de observaciones. Si se concatenan conjuntos Set, el resultado no tendrá elementos repetidos. Si se suman mapas Map y hay claves repetidas en los dos mapas, el valor del segundo es el que prevalece. Por ejemplo

// El elemento 'one' esta repetido con distinto valor
Map map = ['one':1, 'two':2, 'three':3]
Map anotherMap = ['one':-1, 'four':4]

println "Sum of Maps ${anotherMap+map}"      // 'one' contendrá 1, que es lo que dice el segundo sumando, map
println "Sum of Maps ${map+anotherMap}"      // 'one' contendrá -1, que es lo que dice el segundo sumando, anotherMap

Añadir elementos a una coleccion[editar]

A diferencia de lo visto hasta ahora con el operador +, los siguientes métodos modifican la colección original, añadiendo nuevos elementos. Si sólo queremos añadir un elemento al final de la lista, podemos usar el método leftShift() o su equivalente, el operador <<

Estos métodos no funcionan con arrays.

List strings = ['hello','world']
strings << 'one more'

println "one more -> ${strings}"    // Esto da [hello, world, one more]

Si queremos añadir muchos, podemos usar el método addAll()

List strings = ['hello','world']
List moreStrings = ["more hello","more world"]

strings.addAll(moreStrings)
println "addAll() ${strings}"    // Esto da [hello, world, more hello, more world]

Eliminar elementos de una colección[editar]

Una forma de eliminar elementos de una colección (sin modificar la colección original) es usar el operador -, diciendo que elementos queremos borrar

List a = [1,2,3,4]
List b = [2,5]
List result = a - b      // result Contendrá [1, 3, 4]

Modificando la colección original, tenemos métodos

  • removeAll() y sus variantes, que borran los elementos que se indiquen
  • retainAll() y sus variantes, que dejan los elementos que se le indiquen, borrando el resto.
  • unique() y sus variantes, que eliminan duplicados en la colección.

Veamos algunos ejemplos de removeAll()

// Eliminar elementos de a que están en b
List a = [1,2,3,4]
List b = [2,4]
a.removeAll b
println a            // Ahora a contiene [1, 3]

// Eliminar elementos que cumplen una condicion
List a = [1,2,3,4]
a.removeAll { value -> value%2==0}
println a            // Ha borrado los elementos pares (value%2==0), así que contiene [1,3]

retainAll() funciona igual pero al revés, veamos un ejemplo de unique()

List a = [1,1,3,4,5,2,4,1,5,7]
a.unique()
println a           // Ahora contiene [1, 3, 4, 5, 2, 7]

Buscar elementos de una colección[editar]

Los métodos find(), findAll() y findResult() nos permiten buscar el primer elemento que cumpla una condición, buscar todos los elementos que cumplan una condición, o buscar un elemento que cumpla una condición y obtener un resultado calculado a partir de ese elemento. Veamos algunos ejemplos

findAll() devuelve una colección de elementos que cumplan una condición

def a = [0,0,1,2,0,1]

// Devuelve todos los elementos que no sean nulos, 0, false ....
println a.findAll()          // [1, 2, 1]

// Devuelve todos los elementos menores de 2
println a.findAll { element -> element<2}      // [0, 0, 1, 0, 1]

find() devuelve el primer elemento que cumple una condición

def a = [0,0,1,2,0,1]

// Devuelve el primer elemento que no sean nulo, 0, false ....
println a.find()          // 1

// Devuelve el primer elemento menor de 2
println a.find { element -> element<2}      // 0

findResult() permite buscar un elemento con una condición, hacerle una transformación y obtener el transformado

def a = [0,0,1,2,0,1]

// Busca el primer valor mayor que 0 y devuelve el doble.
// Si se devuelve null, se sigue buscando.
// Si se devuelve algo distinto de null, ese es el resultado.
println a.findResult { it>0? it*2 : null }      // 2

// Similar al anterior, pero con un valor por defect (11),
// si no se encuentra ninguno que cumpla la condición.
println a.findResult (11, { it>2? it/2 : null })

Transformar una colección[editar]

Los métodos collect() y collectNested() permiten iterar una colección para transformar sus elementos en otros. La diferencia entre collect() y collectNested() es que si algunos de los elementos de la colección es a su vez otra colección, collectNested() se mete a iterar sobre esos elementos internos, mientras que collect() nos pasará la colección completa como si fuera un item más. Veamos ejemplos


def a = [1,2,3,4,5]

println a.collect { it*2}   // devuelve [2,4,6,8,10]

def b = []
a.collect(b) { it*2}        // mete en la colección b los valores [2,4,6,8,10]
println b                 

a = [1,2,[3,4],5]

// Considera la lista [3,4] como un único item, y [3,4]*2 es [3,4,3,4]
println a.collect { it * 2 }          // Devuelve [2, 4, [3, 4, 3, 4], 10]

// collectNested itera dentro de [3,4]
println a.collectNested { it * 2 }    // Devuelve [2, 4, [6, 8], 10]

Y muchos más métodos[editar]

Tienes muchos métodos más en la api de Collection de Groovy, y como Collection implementa Iterable, hay muchos otros métodos que se heredan y puedes ver en la api de Iterable.

Tienes más ejemplos en la documentación oficial Working with Collections y en esta introducción a Groovy en español.