Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Sessions intra-entreprises sur demande : contact[at]mokatech.net.
Inscrivez-vous vite !

MultiValueMapBuilder

Aujourd'hui, je vous propose une petite classe utilitaire permettant de simplifier l'insertion d'éléments dans des collections (List, Set, Map) elle-mêmes placées dans des Maps.

Ce genre de structure n'est pas rare, dès lors que l'on manipule des données complexes :

  1. Map<City, List<String>> streetNamesByCity;
  2. Map<Department, Set<Employee>> employeesByDepartment;
  3. Map<Employee, Map<Date,VacationType>> vacationsByEmployee;

Insérer un élément dans une sous-collection est simple mais un peu fastidieux et répétitif : il faut vérifier si la sous-collection existe, la créer et l'insérer au besoin, puis -enfin- y insérer le nouvel élément...

Les méthodes statiques de la classe que je vous propose encapsulent cet algorithme :

  1. public static <K,V> boolean putList(Map<K,List<V>> map, K key, V value);
  2. public static <K,V> boolean putSet(Map<K,Set<V>> map, K key, V value);
  3. public static <K,MK,MV> void putMap(Map<K,Map<MK,MV>> map, K key, MK mapKey, MV mapValue);

Examinons le code de la méthode putList :

  1. public static <K,V> boolean putList(Map<K,List<V>> map, K key, V value) {
  2. List<V> list = map.get(key);
  3. if (list == null) {
  4. list = new ArrayList<V>();
  5. map.put(key, list);
  6. }
  7. return list.add(value);
  8. }

Rien de compliqué, mais le fait de l'encapsuler dans une méthode utilitaire vous permet de simplifier votre code appelant :

  1. List<Street> allStreets = streetDao.getAll();
  2. Map<City, List<String>> streetNamesByCity = new HashMap<City, List<String>>();
  3. for(Street street : allStreets) {
  4. MultiValueMapBuilder.putList(streetNamesByCity, street.getCity(), street.getName());
  5. }

Un autre exemple, utilisant une Map :

  1. List<Vacation> employeesVacations = vacationDao.getEmployeesVacations();
  2. Map<Employee, Map<Date,VacationType>> employeeVacationsByDate = new HashMap<Person, Map<Date, Vacation>>();
  3. for (Vacation vacation : vacations) {
  4. MultiValueMapBuilder.putMap(employeeVacationsByDate, vacation.getEmployee(), vacation.getDate(), vacation.getType());
  5. }

Le code source de la classe est disponible en annexe.


Commentaires

1. Le mercredi 16 septembre 2009, 03:09 par Mathieu Carbou

Bonne idée ! Ça m'a fait pensé à une variante, afin d'éviter de coupler tous les endroits dans le code où on souhaite insérer dans la map à une classe statique utilitaire. J'ai fais une variante mais qui utilise les proxy afin de décorer les getters pour créer ces listes automatiquement. Cette variante peut aussi être utilisée pour faire des trucs plus sympa, comme le type de la valeur de la map peut être instancié automatiquement.
Voir http://blog.mycila.com/.

2. Le dimanche 14 février 2010, 11:11 par totolastname

C'est bien mais ça existe déjà dans Google Collections avec la Multimap. Pas la peine de réinventer la roue.

3. Le dimanche 14 février 2010, 15:24 par Olivier Croisier

@totolastname : merci pour cette remarque, je n'avais pas vu.
Effectivement, Google Collections propose ce genre de fonctionnalités via les MultiMaps, avec plein d'options. Si votre projet utilise déjà cette librairie, c'est impeccable, sinon, je pense que cette seule fonctionnalité ne justifie pas l'ajout d'un jar de presque 600k dans le projet, auquel cas ma solution "light" me semble préférable.

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.