2009-05-22 21 views
47

Dado que Java no permite el paso de métodos como parámetros, ¿qué truco utilizas para implementar Python como la comprensión de listas en Java?Comprensión de la lista Python en Java

Tengo una lista (ArrayList) de Strings. Necesito transformar cada elemento usando una función para obtener otra lista. Tengo varias funciones que toman una cadena como entrada y devuelven otra cadena como salida. ¿Cómo hago un método genérico al que se le puede dar la lista y la función como parámetros para que pueda obtener una lista con cada elemento procesado? No es posible en el sentido literal, pero ¿qué truco debería usar?

La otra opción es escribir una nueva función para cada función más pequeña de procesamiento de cadenas que simplemente recorre toda la lista, lo que no es tan genial.

+2

como fyi, puede usar Jython o Scala para obtener listas de comprensión en el JVM – geowa4

+3

... ¡o Clojure! :) – Ashe

+0

SMH al leer todas las respuestas sobre esto. En Python, puede escribir fácilmente una lista de comprensión en una línea de 40-60 caracteres. Todas las soluciones propuestas aquí son múltiples líneas, y la mayoría son más largas que la línea que tomaría en Python. – ArtOfWarfare

Respuesta

33

Básicamente, se crea una interfaz Función:

public interface Func<In, Out> { 
    public Out apply(In in); 
} 

y luego pasar en una subclase anónimo a su método.

Su método podría o bien aplicar la función de cada elemento en el lugar:

public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) { 
    ListIterator<T> itr = list.listIterator(); 
    while (itr.hasNext()) { 
     T output = f.apply(itr.next()); 
     itr.set(output); 
    } 
} 
// ... 
List<String> myList = ...; 
applyToListInPlace(myList, new Func<String, String>() { 
    public String apply(String in) { 
     return in.toLowerCase(); 
    } 
}); 

o crear una nueva List (básicamente la creación de un mapeo de la lista de entrada a la lista de salida):

public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) { 
    List<Out> out = new ArrayList<Out>(in.size()); 
    for (In inObj : in) { 
     out.add(f.apply(inObj)); 
    } 
    return out; 
} 
// ... 
List<String> myList = ...; 
List<String> lowerCased = map(myList, new Func<String, String>() { 
    public String apply(String in) { 
     return in.toLowerCase(); 
    } 
}); 

Cuál es preferible depende de su caso de uso. Si su lista es extremadamente grande, la solución in situ puede ser la única viable; si desea aplicar muchas funciones diferentes a la misma lista original para hacer muchas listas derivadas, querrá la versión map.

+1

Pero luego me pides que coloque todas las funciones pequeñas en una clase diferente ya que tienen que tener un nombre estándar ('aplicar' en tu caso). Correcto ? – euphoria83

+1

No necesariamente; su clase anónima puede simplemente llamar a la pequeña función dentro de apply(). Esto es lo más cercano que Java puede hacer funcionar los punteros sin aventurarse en los peligros de la reflexión. –

+0

doToList está reinventando la rueda. Lo que ha hecho aquí es un diseño pobre de lo que generalmente se llama mapa. La interfaz habitual es pública estática Lista mapa (Lista , Func f); Lo que hace es producir una lista diferente en lugar de modificar la que está en su lugar. Si necesita modificar la lista original sin destruir la referencia, simplemente haga un .clear() seguido de un addAll(). No combine todo eso en un método. – Pyrolistical

16

El Google Collections library tiene muchas clases para trabajar con colecciones e iteradores a un nivel mucho más alto que los soportes de Java simple, y de una manera funcional (filtro, mapa, plegado, etc.). Define las interfaces y los métodos de función y predicado que los utilizan para procesar las colecciones de manera que no sea necesario. También tiene funciones de conveniencia que hacen que lidiar con los genéricos Java sea menos arduo.

También uso Hamcrest ** para filtrar colecciones.

Las dos bibliotecas son fáciles de combinar con las clases de adaptadores.


** Declaración de interés: I co-escribió Hamcrest

+11

Por curiosidad, ¿por qué se llama Hamcrest? Todavía no puedo entender si suena sabroso o no. –

+12

Es un anagrama de "matchers". – Nat

27

En Java 8 se pueden utilizar referencias de métodos:

List<String> list = ...; 
list.replaceAll(String::toUpperCase); 

O, si desea crear una nueva instancia de la lista:

List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList()); 
+7

La pregunta tiene 7 años y Java 8 no existía entonces. Esta debería ser la respuesta aceptada ahora;) – zpontikas

1

estoy construyendo este proyecto de escribir la lista en la comprensión Java, ahora es una prueba de concepto en https://github.com/farolfo/list-comprehension-in-java

Ejemplos

// { x | x E {1,2,3,4}^x is even } 
// gives {2,4} 

Predicate<Integer> even = x -> x % 2 == 0; 

List<Integer> evens = new ListComprehension<Integer>() 
    .suchThat(x -> { 
     x.belongsTo(Arrays.asList(1, 2, 3, 4)); 
     x.is(even); 
    }); 
// evens = {2,4}; 

Y si queremos transformar la expresión de salida de cierta manera como

// { x * 2 | x E {1,2,3,4}^x is even } 
// gives {4,8} 

List<Integer> duplicated = new ListComprehension<Integer>() 
    .giveMeAll((Integer x) -> x * 2) 
    .suchThat(x -> { 
     x.belongsTo(Arrays.asList(1, 2, 3, 4)); 
     x.is(even); 
    }); 
// duplicated = {4,8} 
+1

Parte de la belleza de la comprensión de la lista de Python es lo corto que es. Sus 6 largas líneas de Java podrían escribirse simplemente como '[x * 2 para x en 1, 2, 3, 4 si x% 2 == 0]' ... 1 línea de 41 caracteres. No estoy seguro de cuánto de su código es terrible de leer debido a lo malditamente detallado que es Java vs cuánto es porque su biblioteca no hace las cosas lo suficientemente concisa. – ArtOfWarfare

+0

Todavía es mejor que muchas otras soluciones aquí, de hecho me gusta esto – rhbvkleef

0

Puede utilizar lambdas para la función, así:

class Comprehension<T> { 
    /** 
    *in: List int 
    *func: Function to do to each entry 
    */ 
    public List<T> comp(List<T> in, Function<T, T> func) { 
     List<T> out = new ArrayList<T>(); 
     for(T o: in) { 
      out.add(func.apply(o)); 
     } 
     return out; 
    } 
} 

el uso:

List<String> stuff = new ArrayList<String>(); 
stuff.add("a"); 
stuff.add("b"); 
stuff.add("c"); 
stuff.add("d"); 
stuff.add("cheese"); 
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String> 
    a.equals("a")? "1": 
      (a.equals("b")? "2": 
       (a.equals("c")? "3": 
        (a.equals("d")? "4": a 
    ))) 
}); 

devolverá:

["1", "2", "3", "4", "cheese"] 
Cuestiones relacionadas