2009-04-10 18 views
24

He la clase ViewValue define como sigue:Java - asignar una lista de objetos a una lista con los valores de su propiedad atributos

class ViewValue { 

private Long id; 
private Integer value; 
private String description; 
private View view; 
private Double defaultFeeRate; 

// getters and setters for all properties 
} 

En algún lugar de mi código que necesito para convertir una lista de instancias ViewValue a una lista que contiene los valores de los campos de identificación de ViewValue correspondiente.

lo hago usando bucle foreach:

List<Long> toIdsList(List<ViewValue> viewValues) { 

    List<Long> ids = new ArrayList<Long>(); 

    for (ViewValue viewValue : viewValues) { 
     ids.add(viewValue.getId()); 
    } 

    return ids; 

}

¿Hay una mejor aproximación a este problema?

Respuesta

22

EDITAR: Esta respuesta se basa en la idea de que deberá hacer cosas similares para entidades diferentes y diferentes propiedades en otras partes de su código. Si solo necesita convertir la lista de valores de vista a una lista de Longs por ID, entonces cumpla con su código original. Sin embargo, si desea una solución más reutilizable, siga leyendo ...

Declaro una interfaz para la proyección, p.

public interface Function<Arg,Result> 
{ 
    public Result apply(Arg arg); 
} 

entonces usted puede escribir un único método de conversión genérica:

public <Source, Result> List<Result> convertAll(List<Source> source, 
    Function<Source, Result> projection) 
{ 
    ArrayList<Result> results = new ArrayList<Result>(); 
    for (Source element : source) 
    { 
     results.add(projection.apply(element)); 
    } 
    return results; 
} 

A continuación, puede definir proyecciones simples como esto:

private static final Function<ViewValue, Long> ID_PROJECTION = 
    new Function<ViewValue, Long>() 
    { 
     public Long apply(ViewValue x) 
     { 
      return x.getId(); 
     } 
    }; 

y aplicarlo como este:

List<Long> ids = convertAll(values, ID_PROJECTION); 

(Obvi ormente usando K & R refuerzos y las líneas más largas hace que la declaración de proyección un poco más corto :)

Francamente todo esto sería mucho más agradable con las expresiones lambda, pero no importa ...

+4

Solución muy Javaesque - pero no en el buen sentido. No reduce exactamente la cantidad o complejidad del código, ¿verdad? –

+1

Diría que es más LINQ-esque que Java-esque personalmente. Prefiero crear el código de conversión y luego aislar el aspecto de proyección, pero si prefieres duplicar la lógica de conversión un montón de veces, está bien. Sería mucho mejor con expresiones lambda, por supuesto. –

+1

¿No es algo como lo que hacemos con Comparator en Java? –

2

Eso depende de lo que a continuación, ver con la List<Long> y el List<ViewValue>

Por ejemplo, usted podría conseguir suficiente funcionalidad de crear su propia implementación de lista que se ajusta un List<ViewValue>, implementar iterator() con una implementación iterador que itera sobre los ViewValues, devolver la ID.

3

Se podría UDE un envoltorio:

public class IdList impements List<Long> 
{ 
    private List<ViewValue> underlying; 

    pubic IdList(List<ViewValue> underying) 
    { 
     this.underlying = underying; 
    } 

    public Long get(int index) 
    { 
     return underlying.get(index).getId() 
    } 

    // other List methods 
} 

aunque eso es trabajo aún más tediosa, se podría mejorar el rendimiento.

También podría implementar su solución y la mía de forma genérica utilizando la reflexión, pero eso sería muy malo para la perforación.

No hay una solución genérica corta y fácil en Java, me temo. En Groovy, simplemente usarías collect(), pero creo que también implica reflexión.

3

He implementado una pequeña biblioteca funcional para este uso.Uno de los métodos tiene esta firma:

<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType) 

que toma la cuerda y utiliza la reflexión para crear una llamada a la propiedad, entonces se devuelve una lista apoyada por el ObjectList donde llegar y iterador implementa mediante esta llamada propiedad.

Las funciones mapToProperty se implementan en términos de una función de mapa general que toma una función como asignador, tal como se describe en otra publicación. Muy útil.

le sugiero que lea a la programación básica functionl y, en particular, echa un vistazo a los funtores (objetos que implementan una función de mapa)

Editar: Reflexión en realidad no tiene por qué ser caro. La JVM ha mejorado mucho en esta área. Solo asegúrese de compilar la invocación una vez y volver a utilizarla.

Edit2: Código de ejemplo

public class MapExample { 
    public static interface Function<A,R> 
    { 
     public R apply(A b); 
    } 

    public static <A,R> Function<A,R> compilePropertyMapper(Class<A> objectType, String property, Class<R> propertyType) 
    { 
     try { 
      final Method m = objectType.getMethod("get" + property.substring(0,1).toUpperCase() + property.substring(1)); 

      if(!propertyType.isAssignableFrom(m.getReturnType())) 
       throw new IllegalArgumentException(
        "Property "+property+" on class "+objectType.getSimpleName()+" is not a "+propertyType.getSimpleName() 
       ); 

      return new Function<A,R>() 
      { 
       @SuppressWarnings("unchecked") 
       public R apply(A b) 
       { 
        try { 
         return (R)m.invoke(b); 
        } catch (Exception e) { 
         throw new RuntimeException(e); 
        } 
       }; 
      }; 

     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public static <T1,T2> List<T2> map(final List<T1> list, final Function<T1,T2> mapper) 
    { 
     return new AbstractList<T2>() 
     { 
      @Override 
      public T2 get(int index) { 
       return mapper.apply(list.get(index)); 
      } 

      @Override 
      public int size() { 
       return list.size(); 
      } 
     }; 
    } 

    @SuppressWarnings("unchecked") 
    public static <T1,T2> List<T2> mapToProperty(List<T1> list, String property, Class<T2> propertyType) 
    { 
     if(list == null) 
      return null; 
     else if(list.isEmpty()) 
      return Collections.emptyList(); 

     return map(list,compilePropertyMapper((Class<T1>)list.get(0).getClass(), property, propertyType)); 
    } 
} 
31

Se podía hacerlo de una sola línea usando Commons BeanUtils y Colecciones:
(? ¿Por qué escribir su propio código cuando otros lo han hecho por ti)

import org.apache.commons.beanutils.BeanToPropertyValueTransformer; 
import org.apache.commons.collections.CollectionUtils; 

... 

List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues, 
             new BeanToPropertyValueTransformer("id")); 
+3

+1 para el comentario "¿por qué escribir su propio código cuando otros lo han hecho por usted?" Este es un problema que enfrentamos a diario: tener que crear una solución a un problema que ya se ha resuelto. –

+0

PropertyUtils.getProperty (object, propertyName); utiliza la reflexión debajo del capó, lo que tendría un costo de rendimiento adicional. Para eso, usaría el enfoque de Jon Skeet. –

+4

Lo único que no me gusta de esto es la falta de seguridad del tipo: si se cambiaba el nombre del campo 'viewValue.id', el código aún se compilaría pero fallaría en el tiempo de ejecución. –

24

Usa google collections. Ejemplo:

Function<ViewValue, Long> transform = new Function<ViewValue, Long>() { 
     @Override 
     public Long apply(ViewValue from) { 
      return from.getId(); 
     } 
    }; 
    List<ViewValue> list = Lists.newArrayList(); 
    List<Long> idsList = Lists.transform(list, transform); 

ACTUALIZACIÓN:

En Java 8 que no necesitan de guayaba. Puede:

import com.example.ViewValue; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.function.Function; 
import java.util.stream.Collectors; 

Function<ViewValue, Long> transform = ViewValue::getId; 
List<ViewValue> source = new ArrayList<>(); 
List<Long> result = source.stream().map(transform).collect(Collectors.toList()); 

O simplemente:

List<ViewValue> source= new ArrayList<>(); 
List<Long> result = source.stream().map(ViewValue::getId).collect(Collectors.toList()); 

próxima actualización (la última después de Javaslang a Vavr cambio de nombre):

Actualmente vale la pena mencionar sobre la solución con Biblioteca de Javaslang ( http://www.javaslang.io/) Biblioteca Vavr (http://www.vavr.io/). Asumamos que tenemos nuestra lista con los objetos genuinos:

List<ViewValue> source = newArrayList(new ViewValue(1), new ViewValue(2), new ViewValue(2)); 

podríamos hacer la transformación con la clase de lista de la biblioteca Javaslang (en el largo plazo la recoge no es conveniente):

List<Long> result = io.vavr.collection.List.ofAll(source).map(ViewValue::getId).toJavaList(); 

Pero se quiere ver el poder con sólo las listas Javaslang:

io.vavr.collection.List<ViewValue> source = javaslang.collection.List.of(new ViewValue(1), new ViewValue(2), new ViewValue(3)); 
io.vavr.collection.List<Long> res = source.map(ViewValue::getId); 

animo a echar un vistazo colecciones disponibles y nuevos tipos de esa biblioteca (me gusta especialmente el tipo TRY). Encontrará la documentación en la siguiente dirección: http://www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/.

PS. Debido a que Oracle y la palabra "Java" dentro del nombre tenían que cambiar el nombre de la biblioteca de javaslang a otra cosa. Habían decidido a Vavr.

14

que podemos hacerlo en una sola línea de código usando Java 8

List<Long> ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList()); 

Para más información: Java 8 - Streams

+0

Afortunadamente estamos simplificando las cosas a medida que desarrollamos :) –

+2

Ok, permítanme ponerlo en claro: agregaría comentarios, que esto está habilitado con Java8 que no estaba disponible en el momento de la escritura. La respuesta es en realidad un gran paso hacia adelante a lo que se ha escrito hace 7 años. – Alex

Cuestiones relacionadas