Collections.emptyMap () vs nouveau HashMap ()

143

Quelles sont certaines des situations où je peux utiliser Collections.emptyMap()? La documentation dit que je peux utiliser cette méthode si je veux que ma collection soit immuable.

Pourquoi voudrais-je une collection vide immuable? Dans quel but?

Vinoth Kumar CM
la source
12
+1 à la question parce que je n'ai jamais vu cette méthode statique auparavant
Tim Bender
vous pouvez également jeter un oeil aux lentilles scala lentilles google scala . Le emptyMap et le immutableMap peuvent être utilisés pour créer des objets, qui sont immuables. emptyMap est le point initial. Chaque fois qu'un élément est ajouté, la carte elle-même est remplacée par une carte contenant l'ancien élément et les nouveaux éléments. Le fait est que les accès à l'objet sont sûrs.
michael_s
1
C'est comme l'Objective-C [NSArray array], il renvoie un objet qui, bien que non utilisable, existe. Vous pouvez donc jouer avec lui comme un objet normal et ne pas avoir d'erreur.
Shane Hsu

Réponses:

144

A partir de Effective Java , article n ° 43 - "Return empty arrays or collections, not null"DÉMONTRE retourner une collection vide et peut - être même en utilisant ces DÉMONTRE emptyList(), emptySet()et les emptyMap()méthodes de la classe Collections pour obtenir une collection vide qui a également l'avantage supplémentaire d'être immuable. À partir de l' article n ° 15 "Minimize Mutability" .

De Collections-emptySet-Collections-emptyList-Collections

C'est un type d'idiome de programmation. C'est pour les personnes qui ne veulent pas de variables nulles. Donc, avant que l'ensemble ne soit initialisé, ils peuvent utiliser l'ensemble vide.

Remarque: le code ci - dessous n'est qu'un exemple (modifiez-le en fonction de votre cas d'utilisation):

private Set myset = Collections.emptySet();

void initSet() {
   myset = new HashSet();
}
void deleteSet() {
   myset = Collections.emptySet();
}

Ces méthodes offrent quelques avantages:

  1. Ils sont plus concis car vous n'avez pas besoin de taper explicitement le type générique de la collection - il est généralement simplement déduit du contexte de l'appel de méthode.

  2. Ils sont plus efficaces car ils ne prennent pas la peine de créer de nouveaux objets; ils réutilisent simplement un objet vide et immuable existant. Cet effet est généralement très mineur, mais il est parfois (enfin, rarement) important.

Sumit Singh
la source
15
+1 pour la référence Effective Java. Un point: je recommanderais le paramétrage Setet HashSetdans vos exemples, car tout l'intérêt de la emptySet()méthode et des amis (par opposition aux constantes, Collections.EMPTY_SETetc.) est qu'ils jouent bien avec les génériques. De plus, l'utilisation d'une fonctionnalité (types bruts) qui est obsolète depuis Java 5 n'est pas une bonne aide pédagogique.
Daniel Pryden
4
Je ne suis pas convaincu ... N'est-ce pas tout l'intérêt d'utiliser un Collectionau lieu de nullpour éviter d' Exceptionsêtre jeté sur les opérations suivantes? Utiliser une collection immuable entraînerait simplement une autre sorte d'exception, j'imagine. Et l'attribution nulln'est certainement pas moins efficace que l'attribution d'une constante immuable.
fgysin réinstalle Monica le
14
@fgysin: Si vous avez une API où les clients sont censés modifier la collection, alors oui, cela n'a pas de sens de renvoyer une collection immuable. Mais si vous avez une API où vous retournez une collection que les clients ne doivent pas modifier et doivent simplement itérer, alors renvoyer une vue à la collection sous-jacente est parfaitement logique afin de s'assurer qu'un "mauvais" client ne modifie pas accidentellement les collections possédées par toi. Renvoyer une collection vide au lieu de null signifie que votre client n'a pas à effectuer une vérification null avant d'utiliser la collection, ce qui rend le code client plus clair.
Jean Hominal
4
public boolean setExists() { return !myset.equals(Collections.emptySet()); }
assylias
5
Dans votre exemple, vous vérifiez explicitement l'ensemble vide et l'utilisez comme valeur sentinelle, et votre classe est mutable. Votre petit exemple utilise les sentinelles et la mutabilité, ce que Collections.emptySet () essaie d'empêcher.
Marc O'Morain
33

C'est, d'après mon expérience personnelle, certes, très utile dans les cas où une API nécessite une collection de paramètres, mais vous n'avez rien à fournir. Par exemple, vous pouvez avoir une API qui ressemble à quelque chose comme ceci, et n'autorise pas les références nulles:

public ResultSet executeQuery(String query, Map<String, Object> queryParameters);

Si vous avez une requête qui ne prend aucun paramètre, il est certainement un peu inutile de créer un HashMap, qui consiste à allouer un tableau, alors que vous pouvez simplement passer la `` carte vide '' qui est en fait une constante, la façon dont elle est implémentée dans java.util.Collections.

Affe
la source
22

Pourquoi voudrais-je une collection vide immuable? Dans quel but?

Il y a deux concepts différents ici qui semblent étranges lorsqu'ils sont vus ensemble. Cela a plus de sens lorsque vous traitez les deux concepts séparément.

  • Tout d'abord, vous devriez préférer utiliser une collection immuable plutôt qu'une collection mutable dans la mesure du possible. Les avantages de l'immuabilité sont bien documentés ailleurs .

  • Deuxièmement, vous devriez préférer utiliser une collection vide plutôt que d'utiliser null comme sentinelle. Ceci est bien décrit ici . Cela signifie que vous aurez un code beaucoup plus propre, plus facile à comprendre, avec moins d'endroits pour les bogues à cacher.

Ainsi, lorsque vous avez du code qui nécessite une carte, il est préférable de passer une carte vide plutôt qu'un null pour indiquer l'absence de carte. Et la plupart du temps, lorsque vous utilisez une carte, il est préférable d'utiliser une carte immuable. C'est pourquoi il existe une fonction pratique pour créer une carte vide immuable.

Marc O'Morain
la source
8

Il existe quelques cas où vous préféreriez utiliser des cartes, des listes, des ensembles ou d'autres types de collections immuables.

Le premier cas d'utilisation, et sans doute le plus important, est que chaque fois que vous retournez un résultat d'une requête ou d'un calcul qui renverrait un ensemble (ou une liste ou une carte) de résultats, vous devriez préférer utiliser des structures de données immuables.

Dans ce cas, je préfère de loin renvoyer des versions immuables de ceux-ci car cela reflète l'immuabilité factuelle d'un ensemble de résultats d'un calcul beaucoup plus clairement - peu importe ce que vous faites avec les données plus tard, l'ensemble de résultats que vous avez reçu de votre requête ne devrait pas changement.

Le deuxième cas d'utilisation courant est celui où vous devez fournir un argument en tant qu'entrée d'une méthode ou d'un service. Sauf si vous vous attendez à ce que la collection d'entrée soit modifiée par le service ou la méthode (ce qui est généralement une très mauvaise idée de conception), transmettre une collection immuable au lieu de la collection mutable peut être le choix raisonnable et sûr dans de nombreux cas.

Je pense que c'est une convention «passe par valeur» .

Plus généralement , il est judicieux d'utiliser des structures de données immuables chaque fois que les données franchissent les limites d'un module ou d'un service. Cela rend beaucoup plus facile de raisonner sur les différences entre les entrées / sorties (immuables) et l'état interne mutable.

Un effet secondaire très bénéfique de ceci est une sécurité accrue et la sécurité des threads de vos modules / services et assure une séparation plus nette des problèmes.

Une autre bonne raison d'utiliser les Collections.empty*()méthodes est leur manque notable de verbosité. Dans l'ère pré-Java7, si vous aviez une collection générique, vous deviez saupoudrer des annotations de type générique partout.

Comparez simplement ces deux déclarations:

Map<Foo, Comparable<? extends Bar>> fooBarMap = new HashMap<Foo, Comparable<? extends Bar>>();

contre:

Map<Foo, Comparable<? extends Bar>> fooBarMap = Collections.emptyMap();

Ce dernier gagne clairement en lisibilité de deux manières importantes:

  1. Dans la première déclaration, toute l'instanciation d'une carte vide est enterrée dans le bruit des déclarations de type générique, ce qui rend une déclaration essentiellement triviale beaucoup plus cryptique qu'elle ne devrait l'être.
  2. En plus du manque notable d'annotation de type générique sur le côté droit, la deuxième version indique clairement que la carte est initialisée sur une carte vide. De plus, sachant que cette méthode renvoie une carte immuable, il m'est maintenant plus facile de trouver où fooBarMapest assignée une autre valeur non vide simplement en recherchant /fooBarMap =/.
Roland Tepp
la source
5

D'une part, vous pouvez vous en sortir avec le partage de références. Un new HashMap()etc nécessitera un objet alloué, et éventuellement des éléments supplémentaires pour contenir les données, mais vous n'avez besoin que d'une copie d'une collection vide immuable (liste, ensemble, carte ou autre). Cela en fait un choix évident lorsqu'une méthode que vous appelez doit accepter une carte mais n'a pas besoin de la modifier.

Je suggère de consulter Effective Java de Josh Bloch , qui répertorie de très bons attributs d'objets immuables (y compris la sécurité des threads).

Jeff Bowman
la source
3

Cela peut être utile lorsque vous avez une fonction qui renvoie un immutable collectionet dans certaines situations, il n'y a pas de données à renvoyer, donc au lieu de retourner, nullvous pouvez retourneremptyMap()

Cela facilite votre code et empêche NullPointerException

iTech
la source
3

La plupart du temps, nous utilisons un constructorpour créer un nouveau fichier empty map. Mais l' Collections methodsoffre offre quelques avantages pour créer une empty maputilisationstatic method java.util.Collections.emptyMap()

  1. Ils sont plus concis car vous n'avez pas besoin de taper explicitement le type générique de la collection - il est généralement simplement déduit du contexte de l'appel de méthode.

  2. Ils sont plus efficaces car ils ne prennent pas la peine de créer de nouveaux objets; ils réutilisent simplement un objet vide et immuable existant. Cet effet est généralement très mineur, mais il est parfois (enfin, rarement) important.

subodh
la source
2

Pourquoi voudrais-je une collection vide immuable? Dans quel but?

Pour la même raison que vous utiliseriez Collections.unmodifiableMap()à un moment donné. Vous souhaitez renvoyer une instance de Map qui lève une exception si l'utilisateur tente de la modifier. C'est juste un cas particulier: la carte vide.

Ryan Stewart
la source
1

Pourquoi voudrais-je une collection vide immuable? Dans quel but?

Pour les mêmes raisons que vous pourriez vouloir des objets immuables. Principalement parce que vous pouvez dormir en toute sécurité la nuit en sachant que plusieurs threads peuvent accéder à la même instance d'un objet et qu'ils verront tous les mêmes valeurs. Le fait de n'avoir aucun élément dans une collection est toujours une valeur valide, que vous souhaitez conserver.

vegemite4me
la source