IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API standards et tierces Java Discussion :

Convertir une List en Map


Sujet :

API standards et tierces Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    25
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 25
    Par défaut Convertir une List en Map
    Quelqu'un connait-il un moyen de convertir une liste en map en sélectionnant une propriété en clé ?

    J'ai cherché dans Apache Commons, Spring Utils et Google Guava mais j'ai pas trouvé d'équivalent.

    J'ai donc développé ma fonction :

    Exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    List<Client> clients = Client.findAll();
    Map<Long, Client> clientById = asMap(clients, "id");
    Id étant une propriété de l'objet Client.
    Est ce que quelqu'un connait un équivalent ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    public static <K, V> Map<K, V> asMap(final List<? extends V> items, final String keyProperty) {
    	Map<K, V> result = null;
     
    	if (items != null && !items.isEmpty()) {
    		result = new HashMap<K, V>();
     
    		try {
    			final Class<?> clazz = items.get(0).getClass();
    			final Field fieldKey = clazz.getDeclaredField(keyProperty);
    			fieldKey.setAccessible(true);
     
    			for (final V item : items) {
    				final K key = (K) fieldKey.get(item);
    				result.put(key, item);
    			}
     
    			if (result.size() != items.size()) {
    				LOG.error("asMap : Key Deja existante !");
    				result = null;
    			}
    		} catch (final NoSuchFieldException e) {
    			throw new IllegalArgumentException("La clé " + keyProperty + " est inexistante");
    		} catch (final Exception e) {
    			LOG.error("Exception asMap()", e);
    			result = null;
    		}
     
    	}
    	return result;
    }
    PS : le get(0) ne me plait guère.
    870ms pour 1 000 000 d'items.

  2. #2
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,


    Ca doit surement exister dans ces APIs de collection, mais je ne les connais pas suffisamment...


    En effet le get(0) n'est pas top : cela pourrait engendrer des erreurs dans le cas où la liste comporte des types différents.

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        List<Number> list = new ArrayList<Number>();
        list.add( 10 ); // Integer
        list.add( 10.0 ); // Double
     
        Map<Number, Number> map = asMap(list, "value");
    Les types Integer et Double possèdent bien un champ "value", mais ce n'est pas le même ce qui engendre une erreur...

    Il serait préférable de passer la classe de base en paramètre, afin d'être sûr de taper dans quelque chose qui existe bel et bien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public static <K, V> Map<K, V> asMap(final List<? extends V> items, Class<V> clazz, final String keyProperty)
    De même plutôt que de passer par les Fields, il serait plus propre de passer par un accesseur via les Methods (ce qui permet au passage d'éviter également le setAccessible()).




    Attention également à ce bout de code anodin :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        } catch (final Exception e) {
            result = null;
        }
    En cas d'erreur tu retournes null... sans rien comprendre. Le problème c'est que du coup tu peux passer complètement à travers...
    Il serait préférable de remonter une RuntimeException, afin d'avoir toutes les infos utiles si jamais l'erreur se produit (ca fera planter le programme, mais avec un joli stacktrace et une exception claire et net) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }



    Enfin dernier problème signalé par le warning sur cette ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    final K key = (K) fieldKey.get(item);
    Le compilateur ne peut pas vérifier la cohérence des types. Donc en cas d'erreur il est possible de créer une Map incorrect, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Map<Long, Client> clientById = asMap(clients, "date");
    L'attribut "date" est une Date, mais on le déclare en Long.
    Cela fonctionne sans erreur mais on reçoit une Map<Date,Client> déclarée en Map<Long, Client> ce qui va provoquer des erreurs inattendus lorsqu'on l'utilisera... et un bon casse-tête pour en comprendre l'origine





    La solution la plus sécurisée consiste à passer via une interface définie comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public interface GetProperty<T, A> {
        public A get(T object);
    }
    Cette interface permettra d'obtenir une valeur à partir d'un objet.


    Il suffit ensuite d'utiliser cette interface pour accéder aux champs souhaités, ce qui nous donne une méthode asMap() plutôt simplifiée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        public static <K, V> Map<K, V> asMap(final List<? extends V> items, GetProperty<V, K> getKey) {
            Map<K, V> result = null;
     
            if (items != null && !items.isEmpty()) {
                result = new HashMap<K, V>();
                for (final V item : items) {
                    result.put(getKey.get(item), item);
                }
            }
            return result;
        }
    Après c'est sûr que l'appel est un peu plus "long" à écrire (vivement Java 8 bordel) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Map<Long, Client> clientById = asMap(clients,
        new GetProperty<Client, Long>() {
            @Override
            public Long get(Client c) {
                return c.getId();
            }
        });
    Mais c'est nettement plus propre (le compilateur détectera les erreurs de type) et plus rapide que la reflection qui s'avère assez "lourde" (environ 3x plus rapide chez moi).



    a++

  3. #3
    Membre Expert
    Inscrit en
    Août 2009
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Août 2009
    Messages : 1 073
    Par défaut
    J'avais gardé ce post dans un coin de mémoire, en me disant que ça devait bien se trouver, une telle méthode ...
    Mais rien sur le Net, à part des gens qui proposent en fait des méthodes du type de celle d'adiGuba.
    Je trouve ça franchement étonnant, mais ça semble faire partie des choses qu'on re-développe dans chaque projet ...

  4. #4
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 582
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 582
    Par défaut
    J'ai pourtant pas souvent besoin de ça. A priori si ça doit pouvoir prendre la forme d'une Map ça n'a jamais existé sous une autre forme qu'une Map. Je chercherais les raisons pour que ça soit pas le cas, et en général ces raisons existent.

    Un truc que je fais de temps en temps, C'est passer de List<V> à Map<K, Collection<V>>. Là oui, on est souvent passé d'abord par une représentation en List plate, puis après on indexe les éléments de la liste, mais il y en a plus d'un par index.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    25
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 25
    Par défaut
    Merci pour ta proposition adiGuba. (et merci pour les conseils)

    Après avoir épluché Google Guava, une librairie vraiment sympa je trouve au passage, je suis tombé sur ta proposition :


    La méthode Maps.uniqueIndex reprends ce principe :
    API com.google.common.collect.Maps.uniqueIndex
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Map<K,V> uniqueIndex(Iterable<V> values, Function<? super V,K> keyFunction);
    Et pour thelvin qui désire passer d'une List<V> à une Map<K, Collection<V>> Google Guava t'aide aussi :

    La méthode Multimaps.index réalise cette transformation :
    API com.google.common.collect.Multimaps.index
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ImmutableListMultimap<K,V> index(Iterable<V> values, Function<? super V,K> keyFunction)
    une ImmutableListMultimap peut devenir une Map<K, Collection<V>> grâce à sa méthode asMap.

Discussions similaires

  1. convertir une liste en string
    Par tntneo dans le forum Prolog
    Réponses: 2
    Dernier message: 03/04/2010, 02h24
  2. Réponses: 3
    Dernier message: 18/06/2009, 15h59
  3. Convertir une List<Object> en List<Toto>
    Par onlytoine dans le forum Langage
    Réponses: 7
    Dernier message: 19/01/2009, 14h37
  4. COnvertir une Collection en Map
    Par Tiaps dans le forum Collection et Stream
    Réponses: 2
    Dernier message: 26/02/2008, 08h31
  5. Convertir une liste en prédicat
    Par Myrkvid dans le forum Prolog
    Réponses: 2
    Dernier message: 10/11/2006, 09h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo