2010-06-24 16 views
6

Digamos que tengo un frijol como el siguiente.Obtener una lista de elementos de un ArrayList

class Customer{ 
    private String code; 
    private String name; 
    private Integer value; 
    //getters setters omitted for brevity 
} 

Luego, de un método me sale un List<Customer> hacia atrás. Ahora digamos que quiero obtener una lista de todos los miembros de "nombre" de la lista. Obviamente puedo atravesar y construir un List<String> del elemento "nombre" yo mismo.

Sin embargo, me preguntaba si hay un atajo o una forma más eficiente de esta técnica que cualquiera sepa. Por ejemplo, si quiero obtener una lista de todas las claves en un objeto Map, obtengo do map.keySet(). Algo en esa línea es lo que trato de descubrir.

+0

Creo que estás atrapado en bucle, o si eres aventurero, puedes echar un vistazo a [quaere] (http://xircles.codehaus.org/projects/quaere). Eche un vistazo a [¿Cuál es el equivalente de Java para LINQ?] (Http://stackoverflow.com/questions/1217228/what-is-the-java-equivalent-for-linq) para obtener información relacionada. – R0MANARMY

+0

Creo que no está buscando una forma más eficiente, pero quizás de una manera más eficiente. El for-loop predeterminado ya es bastante eficiente. – OscarRyz

+0

Bueno, no necesariamente en cuanto a la eficacia, pero estaba tratando de hacerme una idea de lo que otros hacen por las tareas rutinarias como esta. Aprendí sobre Guava y Lambdaj son algunas de las alternativas populares. ¡Así que eso es bueno! – CoolBeans

Respuesta

6

guayaba tiene Lists.transform que puede transformar un List<F> a un List<T>, utilizando un Function<F,T> proporcionado (o más bien, Function<? super F,? extends T>).

De la documentación:

public static <F,T> 
    List<T> transform(
       List<F> fromList, 
       Function<? super F,? extends T> function 
      ) 

Devuelve una lista que se aplica a cada elemento function de fromList. La lista devuelta es una vista transformada de fromList; los cambios a fromList se reflejarán en la lista devuelta y viceversa.

El function se aplica de forma diferida, se invoca cuando es necesario.

vivo-vista similar transforma también se proporcionan los siguientes:

+0

Genial, es bueno ver un enlace a una biblioteca que hace básicamente lo que estaba sugiriendo en mi publicación. –

+0

@Mark: la gran diferencia es que la implementación de Guava aplica la función bajo demanda. Una especie de "transformada perezosa", como la tuya está ansiosa. – polygenelubricants

+0

Ah, tenía curiosidad sobre por qué estaban usando específicamente iterable. Eso explica el razonamiento. Me gusta aún más ahora (¿asumiendo que hay un método de fábrica en Guava para crear una colección de un iterable?) –

3

Creo que esto es algo que tendría que codificar usted mismo, en un bucle.

+1

Con LambdaJ solo tiene que escribir el convertidor Cliente-> Cadena –

+2

O mejor aún ;-) Listas de guayaba.transformar http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect /Lists.html#transform(java.util.List, com.google.common.base.Function) –

5

Parece que está buscando el equivalente en Java de la función map de Perl. Este tipo de cosas podrían agregarse a la biblioteca de colecciones una vez que Java reciba cierres. Hasta entonces, creo que esto es lo mejor que puede hacer:

List<String> list = new ArrayList<String>(customers.size()); 
for (Customer c : customers) { 
    list.add(c.getName()); 
} 

También podría escribir una función map que utiliza una interfaz sencilla para proporcionar la función de mapeo. Algo como esto:

public interface Transform<I, O> { 
    O transform(I in); 
} 
public <I, O> List<O> map(Collection<I> coll, Transform<? super I, ? extends O> xfrm) { 
    List<O> list = new ArrayList<O>(coll.size()); 
    for (I in : coll) { 
     list.add(xfrm.transform(in)); 
    } 
    return list; 
} 
+0

Función de mapa "Perl"? Jeje! ¿No te refieres al equivalente de Java del equivalente de Perl de la función de mapa de Lisp? – dsmith

2

Puede utilizar LambdaJ 's Converter interfaz y tiene la siguiente línea:

List<String> customerNames = convert(customerList, new Converter<Customer,String>() { 
    public String convert(Customer customer) { 
    return customer.getName(); 
    } 
}); 
+0

Interesante. ¿Sabes si funciona con jdk 1.5 o solo es Java 6? – CoolBeans

+0

AFAIK solo necesita Java 5 –

2

Es necesario utilizar un bucle, pero la función que está buscando se llama en map lenguajes funcionales. Es posible implementar map en Java, aunque tiende a ser bastante poco elegante; Aquí está la versión I implementado hace años en mi "cosas de Java debe tener, pero por alguna razón no" biblioteca:

public interface MapFunction<T, U> { 
    public U map(T source); 
} 

public static <T, U> U[] map(T[] objects, MapFunction<T, U> f) { 
    if(objects.length == 0) {throw new IllegalArgumentException("Can't map onto an empty array");} 
    @SuppressWarnings("unchecked") U[] rtn = (U[])Array.newInstance(f.map(objects[0]).getClass(), objects.length); 
    for(int i = 0; i < objects.length; i++) 
     rtn[i] = f.map(objects[i]); 
    return rtn; 
} 

Usando eso, usted podría hacer:

List<Customer> list = yourFunction(); 
List<String> names = Arrays.asList(map(list.toArray(new Customer[0]), new MapFunction<Customer, String>() { 
    public String map(Customer c) { 
     return c.getName(); 
    } 
})); 

Se podría, naturalmente, cambiar mapa tomar colecciones en lugar de matrices, lo que eliminaría la necesidad de Arrays.asList y List.toArray

2

Usando Guava, se puede utilizar un Function junto con Iterables.transform, o Collections2.transformLists.transform para crear un Iterable, Collection o List respectivamente.

Iterable<String> names = Iterables.transform(customers, 
    new Function<Customer, String>() { 
     public String apply(Customer from) { 
     return from.getName(); 
     } 
    }); 

El regresaron Iterable es perezoso y se aplica la función a la lista subyacente como iterar a través de él. Para una List<String> que contiene los nombres, se puede utilizar:

List<String> names = Lists.transform(...); 

o

ImmutableList<String> names = ImmutableList.copyOf(Iterables.transform(...)); 

Por supuesto, escribir a cabo la clase interna anónima Function aplicación cada vez que desee hacer esto es feo y detallado, por lo Es posible que desee hacer que el Function sea una constante disponible de la clase Customer, llamada Customer.NAME por ejemplo.

A continuación, la transformación se verá mejor (con importaciones estáticas especialmente):

for (String name : transform(customers, Customer.NAME)) { ... } 

También he escrito sobre el uso de las interfaces para las propiedades particulares de los objetos (como name aquí) para ayudar con la consolidación de estas funciones en mi blog here.

+0

Bien, tanto usted como Polygene dieron respuestas similares. Si pudiera aceptar dos respuestas como correctas, también habría aceptado la tuya. ¡Gracias! – CoolBeans

1

....¿hay un atajo o una manera más eficiente

Entonces, ¿estás buscando una manera más eficiente de hacer esto:

List<String> names = new ArrayList<String>(); 
for(Customer customer : yourCustomerList) { 
    names.add(customer.getName()); 
} 

!!!!?

¿O solo un diferente manera?

Todas las respuestas anteriores no son realmente más eficientes en términos de tiempo de ejecución ni codificación. Sin embargo, son más flexibles sin lugar a dudas.

Otra alternativa sería incluir Scala maravilloso en el código Java y utilizar esto:

list.map(_.name)

list.collect { it.name } 

Si es compilado, clases Groovy se puede utilizar desde Java, o puede enchúfalos como un script.

Aquí hay un ejemplo de la clase dada Customer usando Groovy como secuencia de comandos.

List<Customer> customers = Arrays.asList(new Customer[]{ 
     new Customer("A","123",1), 
     new Customer("B","456",2), 
     new Customer("C","789",3), 
     new Customer("D","012",4) 
    }); 

    setVariable(customers, "list"); 
    evaluate("names = list.collect { it.name } "); 
    List<String> names = (List<String>) getVariable("names"); 
    System.out.println("names = " + names); 

Salida:

names = [A, B, C, D] 

nota: extraje método para facilitar la lectura, pero se pueden ver a continuación

Pero, una vez más que es simplemente diferente, no es realmente más eficaz que la normal en bucle.

Aquí está el complete source code. Para ejecutarlo solo necesitas Java1.6 y Groovy en el classpath.

+0

Gracias, he oído hablar de Groovy pero aún no lo he metido en problemas. Esto se ve limpio. – CoolBeans

Cuestiones relacionadas