Java-mapper une liste d'objets à une liste avec les valeurs de leurs attributs de propriété


J'ai la classe ViewValue définie comme suit:

class ViewValue {

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

// getters and setters for all properties
}

Quelque part dans mon code, j'ai besoin de convertir une liste d'instances ViewValue en une liste contenant les valeurs des champs id de ViewValue correspondant.

Je le fais en utilisant foreach loop:

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

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

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

   return ids;

}

Est-il une meilleure approche de ce problème?

Author: mgamer, 2009-04-10

7 answers

EDIT: Cette réponse est basée sur l'idée que vous devrez faire des choses similaires pour différentes entités et différentes propriétés ailleurs dans votre code. Si vous seulement avez besoin de convertir la liste de ViewValues en une liste de Longs par ID, respectez votre code d'origine. Si vous voulez une solution plus réutilisable, cependant, lisez la suite...

Je déclarerais une interface pour la projection, par exemple

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

Ensuite, vous pouvez écrire une seule méthode de conversion générique:

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;
}

Alors vous pouvez définir des projections simples comme ceci:

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

Et l'appliquer comme ceci:

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

(Évidemment, l'utilisation de contreventements K&R et de lignes plus longues rend la déclaration de projection un peu plus courte :)

Franchement, tout cela serait beaucoup plus agréable avec des expressions lambda, mais peu importe...

 25
Author: Jon Skeet, 2009-04-10 10:57:37

Vous pouvez le faire dans un one-liner en utilisant Commons BeanUtils et Collections:
(pourquoi écrire votre propre code quand d'autres l'ont fait pour vous?)

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

...

List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues, 
                                       new BeanToPropertyValueTransformer("id"));
 32
Author: Ulf Lindback, 2009-10-30 08:52:55

Utilisez Google collections. Exemple:

    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);

MISE À JOUR:

Sur Java 8, vous n'avez pas besoin de goyave. Vous pouvez:

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());

Ou juste:

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

PROCHAINE MISE À JOUR (La dernière après le changement de nom Javaslang à Vavr):

Actuellement, il vaut la peine de mentionner la solution avec Javaslang library(http://www.javaslang.io/) Bibliothèque Vavr (http://www.vavr.io / ). Supposons que nous avons notre liste avec objets:

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

Nous pourrions faire une transformation avec la classe List de la bibliothèque Javaslang (à long terme, la collecte n'est pas pratique):

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

Mais vous verrez la puissance avec seulement les listes 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);

J'encourage à jeter un oeil aux collections disponibles et aux nouveaux types sur cette bibliothèque (j'aime particulièrement le type Try). Vous trouverez la documentation sous l'adresse suivante: http://www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/.

PS. En raison de l'Oracle et du mot "Java" dans le nom, ils ont dû changer le nom de la bibliothèque de javaslang à autre chose. Ils avaient décidé de Vavr.

 28
Author: Przemek Nowak, 2017-09-26 17:53:02

Nous pouvons le faire en une seule ligne de code en utilisant java 8

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

Pour plus d'informations : Java 8-Flux

 22
Author: Radhakrishna Sharma Gorenta, 2017-02-07 14:23:22

J'ai implémenté une petite bibliothèque fonctionnelle pour ce cas d'utilisation. Une des méthodes a cette signature:

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

Qui prend la chaîne et utilise la réflexion pour créer un appel à la propriété, puis renvoie une liste soutenue par objectList où get et iterator sont implémentés à l'aide de cet appel de propriété.

Les fonctions mapToProperty sont implémentées en termes de fonction de carte générale qui prend une fonction comme mappeur, tout comme un autre article décrit. Très utile.

Je vous suggère de lire sur la programmation functionl de base et en particulier de jeter un œil aux foncteurs (objets implémentant une fonction map)

Edit: La réflexion n'a vraiment pas à être chère. La JVM s'est beaucoup améliorée dans ce domaine. Assurez-vous simplement de compiler l'invocation une fois et de la réutiliser.

Edit2: Exemple de code

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));
    }
}
 4
Author: John Nilsson, 2009-04-12 00:32:28

Vous pouvez utiliser un wrapper:

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
}

Bien que ce soit un travail encore plus fastidieux, cela pourrait améliorer les performances.

Vous pouvez également implémenter votre solution et ma solution de manière générique en utilisant la réflexion, mais cela serait très mauvais pour la perforance.

Il n'y a pas de solution générique courte et facile en Java, j'ai peur. Dans Groovy, vous utiliseriez simplement collect (), mais je crois que cela implique également une réflexion.

 3
Author: Michael Borgwardt, 2009-04-10 10:33:17

, ce Qui dépend de ce que vous faites avec le List<Long> et le List<ViewValue>

Par exemple, vous pouvez obtenir une fonctionnalité suffisante en créant votre propre implémentation de liste qui encapsule un List<ViewValue>, en implémentant iterator() avec une implémentation d'itérateur qui itère sur les ViewValues, renvoyant l'id.

 2
Author: Stephen Denne, 2009-04-10 10:31:07