Pourquoi ne HashSet mise en œuvre dans Sun Java utiliser HashMap que sa sauvegarde?
En regardant la source de Java 6, HashSet<E>
est réellement mis en œuvre à l'aide de HashMap<E,Object>
, à l'aide de mannequin instance d'objet sur chaque entrée de l'Ensemble.
Je pense que les déchets 4 octets (sur les machines 32 bits) pour la taille de l'entrée elle-même.
Mais, pourquoi est-il encore utilisé? Est-il une raison pour l'utiliser en plus de rendre plus facile à maintenir les codes?
- le niveau de déchets en Java par défaut des collections est aberrant. Le pire des délinquants qui se passe quand vous manipulez des primitives. Vous pensez qu'un HashSet<Object> qui est mauvais? Pas de penser à ce sujet: HashMap<Integer,Integer>. Si vous êtes après, efficace collections que vous voulez regarder Trésor (pour les primitives) ou Javolution (en temps réel). Ils tournent tous deux autour de cercles de Java par défaut collections, à la fois la performance et la mémoire sage. Nous faisons de lourds calculs et des collections avec des millions d'éléments sont communs pour nous. Mine de roches. Javolution rochers. La valeur par défaut de Java collections n'ont tout simplement pas le couper.
- pour continuer sur mon commentaire... Ce que je veux dire, c'est: soit perfs et de la mémoire de la matière et ensuite, vous devez trouver une autre alternative, car le niveau de déchets en Java par défaut des collections est trop élevée ou que vous n'avez pas besoin de perfs et de la mémoire n'a pas d'importance, parce que vous allez être à l'aide de minuscules nombre d'éléments et la valeur par défaut de Java collections sont ok (dur il y a probablement une meilleure solution alternative comme le Google collections etc.)
- c'est beaucoup de déclarations audacieuses avec peu de preuves pour les sauvegarder.
- HashMap ne permet pas de doubles de clés donc à l'aide d'une table de hachage pour mettre en œuvre un ensemble est une bonne idée. Ils auraient voulu réutiliser le code existant de table de hachage.
Vous devez vous connecter pour publier un commentaire.
En fait, ce n'est pas seulement
HashSet
. Tous les implémentations de laSet
interface en Java 6 sont basés sur un sous-jacentMap
. Ce n'est pas une obligation, c'est juste la manière dont l'application est. Vous pouvez voir par vous-même en consultant la documentation pour les diverses implémentations deSet
.Vos principales questions sont
Je suppose que le code de la maintenance est un grand facteur de motivation. Donc est d'empêcher la duplication et le ballonnement.
Set
etMap
sont similaires interfaces, en ce que les éléments en double ne sont pas autorisés. (Je pense que la seuleSet
pas soutenu par uneMap
estCopyOnWriteArraySet
, qui est une étonnante Collection, parce qu'il est immuable.)Spécifiquement:
De la la documentation de
Set
:Et de
Map
:Si vous pouvez mettre en œuvre votre
Set
s à l'aide du code existant, tout avantage (vitesse, par exemple), vous pouvez réaliser à partir d'un code existant revient à votreSet
ainsi.Si vous choisissez de mettre en œuvre un
Set
sansMap
la sauvegarde, vous devez dupliquer du code conçu pour empêcher les éléments en double. Ah, la délicieuse ironie.Cela dit, rien ne vous empêche de mise en œuvre de votre
Set
s différemment.Set
interface en Java 6 sont basés sur un sous-jacentCollection
." (Je suppose que vous voulez direMap
au lieu deCollection
.) Il existe au moins un contre-exemple (autres que les sous-ensembles et autres).EnumSet
n'est pas basé sur unMap
.Je devine qu'il n'a jamais tourné comme un problème important pour les applications réelles ou des points de repère importants. Pourquoi compliquer le code pour aucun avantage réel?
Est aussi à noter que l'objet tailles sont arrondis dans de nombreux JVM mise en œuvre, de sorte qu'il peut ne pas être en fait une augmentation de la taille (je ne sais pas pour cet exemple). Le code de
HashMap
est susceptible d'être compilé et dans le cache. Autres choses étant égales par ailleurs, plus de code => plus de défauts de cache => une baisse des performances.Ma conjecture est que HashSet a été initialement mis en œuvre en termes de table de hachage afin de le faire rapidement et facilement. En termes de lignes de code, HashSet est une fraction de la table de hachage.
Je suppose que la raison pour laquelle il n'a pas encore été optimisé, c'est la peur du changement.
Toutefois, les déchets sont bien pire que vous le pensez. Sur les versions 32-bit et 64-bit, HashSet est 4x plus grande que nécessaire, et HashMap est 2x plus grande que nécessaire. HashMap pourraient être mises en œuvre avec un tableau avec les clés et les valeurs qu'il contient (en plus de chaînes pour les collisions). Cela signifie que deux pointeurs par entrée, ou 16 octets sur une version 64 bits de VM. En fait, la table de hachage contient une Entrée par entrée, qui ajoute 8 octets pour le pointeur à l'Entrée et 8 octets pour l'Entrée d'en-tête objet. HashSet utilise également 32 octets par élément, mais les déchets est de 4x au lieu de 2x, car il ne nécessite 8 octets par élément.
key
,value
, et unnext
entrée pour la gestion des collisions a cinq fois plus d'espace par rapport à une référence unique dans un tableau plat (quand on compare avec un possibleSet
mise en œuvre). Mais il y a encore un tableau à l'intérieur deHashMap
trop, le tableau de références àEntry
instances. Donc en fin de compte, laHashMap
en fonctionHashSet
prend environ six fois en l'espace d'un tableau plat à baseHashSet
. Sur une version 64 bits de la JVM HotSpot avec CompressedOOPs et CompressedKlassPointers activé, c'est même 6,5 fois...HashMap
changé de manière significative au cours de la dernière décennie. Et je ne comprends pas pourquoi vous insistez de manière agressive sur le rejet de la possibilité que l'amélioration peut être même mieux dans certains scénarios. C'est que “4x.” une libération conditionnelle donnée par la sainte dictateur qui l'emporte sur toutes les techniques de discussion ou quoi?Oui, vous avez raison, une petite quantité de gaspillage est definetley là. Petite parce que, pour chaque entrée, il utilise le même objet
PRESENT
(qui est déclarée final). Par conséquent, la seule gaspillage est pour chaque entrée de la valeur dans la table de hachage.Surtout, je pense, ils ont adopté cette approche pour la maintenabilité et la réutilisabilité. (Le JCF les développeurs ont pensé, nous avons testé HashMap de toute façon, pourquoi ne pas réutiliser.)
Mais si vous avez de grandes collections, et vous êtes un mémoire freak, alors vous pouvez opter pour les meilleures solutions de rechange comme Trove ou Google Collections.
J'ai regardé votre question et il m'a fallu un peu de temps pour réfléchir sur ce que vous avez dit. Voici donc mon avis sur le
HashSet
mise en œuvre.Il est nécessaire d'avoir le mannequin exemple pour savoir si la valeur est ou n'est pas présent dans le jeu.
Prendre un coup d'oeil à la méthode add
Abd maintenant, nous allons jeter un oeil à le mettre valeur de retour
De sorte que le
PRESENT
objet est utilisée pour représenter l'ensemble contient la valeur de e. Je pense que vous avez demandé pourquoi ne pas utilisernull
au lieu dePRESENT
. Mais la, vous ne seriez pas en mesure de distinguer si l'entrée était déjà sur la carte parce quemap.put(key,value)
serait toujours revenirnull
et vous n'auriez pas moyen de savoir si la clé existe.Cela étant dit, on pourrait dire qu'ils auraient pu utiliser une application comme ceci
Je suppose qu'ils déchets 4 octets pour éviter le calcul de la hashCode, comme il pourrait l'être cher, de la clé deux fois (si la clé va être ajoutée).
Si vous la question de pourquoi ils ont utilisé un
HashMap
qui feraient perdre 8 octets (en raison de laMap.Entry
) à la place d'une autre structure de données à l'aide d'une Entrée similaire de seulement 4, alors oui, je dirais qu'ils l'ont fait pour les raisons que vous avez mentionnées.Après la recherche par le biais des pages comme ça vous vous demandez pourquoi le légèrement inefficace standard de mise en œuvre, trouve com.carrotsearch.des professions.IntOpenHashSet
Votre question:
Je pense que les déchets 4 octets (sur les machines 32 bits) pour la taille de l'entrée elle-même.
Juste une variable Objet est créé pour l'ensemble de la discbased de hashset et de faire qui permettrait de sauver de la ré-écriture de l'ensemble de la table de hachage type de code à nouveau.
private static final Object PRESENT = new Object();
Toutes les touches sont ayant une valeur que j'ai.e objet.
value
champ pour toutes les entrées dans laHashSet
.