Depuis Java 5, nous avons boxé / déballé des types primitifs pour que cela int
soit enveloppé java.lang.Integer
, et ainsi de suite.
Je vois beaucoup de nouveaux projets Java ces derniers temps (qui nécessitent définitivement un JRE d'au moins la version 5, sinon 6) qui utilisent int
plutôt que java.lang.Integer
, bien qu'il soit beaucoup plus pratique d'utiliser ce dernier, car il a quelques méthodes d'aide pour la conversion aux long
valeurs et al.
Pourquoi certains utilisent encore des types primitifs en Java? Y a-t-il un avantage tangible?
java
primitive
primitive-types
autoboxing
jdk1.5
Naftuli Kay
la source
la source
new IntegeR(5) == new Integer(5)
les règles devraient être évaluées à false.Réponses:
Dans Effective Java de Joshua Bloch , article 5: «Évitez de créer des objets inutiles», il publie l'exemple de code suivant:
et il faut 43 secondes pour fonctionner. Prendre le Long dans la primitive le ramène à 6,8 secondes ... Si cela indique pourquoi nous utilisons des primitives.
Le manque d'égalité des valeurs natives est également un problème (
.equals()
est assez verbeux par rapport à==
)pour biziclop:
Résulte en:
EDIT Pourquoi (3) retourne-t-il
true
et (4) revient-ilfalse
?Parce que ce sont deux objets différents. Les 256 entiers les plus proches de zéro [-128; 127] sont mis en cache par la JVM, ils renvoient donc le même objet pour ceux-ci. Au-delà de cette plage, cependant, ils ne sont pas mis en cache, donc un nouvel objet est créé. Pour compliquer les choses, le JLS exige qu'au moins 256 poids mouches soient mis en cache. Les implémenteurs de JVM peuvent en ajouter plus s'ils le souhaitent, ce qui signifie que cela pourrait fonctionner sur un système où les 1024 les plus proches sont mis en cache et tous retournent true ... #awkward
la source
i
étaient également déclaréesLong
!==
opérateur effectue des comparaisons d'identité de référence sur lesInteger
expressions et des comparaisons d'égalité de valeur sur lesint
expressions.Integer.equals()
existe précisément pour cette raison. Vous ne devez jamais utiliser==
pour comparer des valeurs dans un type non primitif. Ceci est Java 101.L'autounboxing peut conduire à des NPE difficiles à repérer
Dans la plupart des situations, l'affectation nulle à
in
est beaucoup moins évidente que ci-dessus.la source
Les types en boîte ont de moins bonnes performances et nécessitent plus de mémoire.
la source
Types primitifs:
Évaluez maintenant:
C'est
true
. Pas étonnant. Maintenant, essayez les types en boîte:Évaluez maintenant:
C'est
false
. Probablement. Dépend du runtime. Cette raison est-elle suffisante?la source
Outre les problèmes de performances et de mémoire, j'aimerais proposer un autre problème: l'
List
interface serait cassée sansint
.Le problème est la
remove()
méthode surchargée (remove(int)
vs.remove(Object)
).remove(Integer)
se résoudrait toujours à appeler ce dernier, vous ne pouvez donc pas supprimer un élément par index.D'autre part, il y a un piège lorsque vous essayez d'ajouter et de supprimer un
int
:la source
Vector
avaitremoveElementAt(int)
dès le début.remove(int)
a été introduit avec le framework de collections en Java 1.2.List
API a été conçue, ni les génériques ni l'Autoboxing n'existaient, donc il n'y avait aucune chance de se mélangerremove(int)
etremove(Object)
…Pouvez-vous vraiment imaginer un
boucle avec java.lang.Integer à la place? Un java.lang.Integer est immuable, donc chaque incrément autour de la boucle créerait un nouvel objet java sur le tas, plutôt que de simplement incrémenter l'int sur la pile avec une seule instruction JVM. La performance serait diabolique.
Je ne suis vraiment pas d'accord pour dire qu'il est beaucoup plus pratique d'utiliser java.lang.Integer que int. Au contraire. Autoboxing signifie que vous pouvez utiliser int là où vous seriez autrement obligé d'utiliser Integer, et le compilateur java se charge d'insérer le code pour créer le nouvel objet Integer pour vous. Autoboxing consiste à vous permettre d'utiliser un int là où un entier est attendu, le compilateur insérant la construction d'objet appropriée. Cela ne supprime ni ne réduit en aucun cas le besoin de l'int en premier lieu. Avec la boxe automatique, vous obtenez le meilleur des deux mondes. Vous obtenez un entier créé automatiquement pour vous lorsque vous avez besoin d'un objet Java basé sur un tas, et vous obtenez la vitesse et l'efficacité d'un int lorsque vous ne faites que des calculs arithmétiques et locaux.
la source
Les types primitifs sont beaucoup plus rapides:
Integer (tous les nombres et aussi une chaîne) est un type immuable : une fois créé, il ne peut pas être modifié. Si
i
était Integer,i++
cela créerait un nouvel objet Integer - beaucoup plus cher en termes de mémoire et de processeur.la source
i++
sur une autre variable, donc Integer doit tout à fait être immuable pour pouvoir le faire (ou du moins celai++
devrait créer un nouvel objet Integer de toute façon). (Et les valeurs primitives sont également immuables - vous ne le remarquez tout simplement pas car ce ne sont pas des objets.)++
est un hareng rouge ici. Imaginez Java a été amélioré pour l' opérateur de soutien surcharge d'une manière très simple, de sorte que si une classe ( par exempleInteger
a une méthodeplus
, alors vous pouvez écrire aui + 1
lieu dei.plus(1)
. Et aussi supposer que le compilateur est assez intelligent pour développeri++
eni = i + 1
. Maintenant , vous pourriez direi++
et effectivement "incrémenter la variable i" sansInteger
être mutable.D'abord et avant tout, l'habitude. Si vous codez en Java depuis huit ans, vous accumulez une quantité considérable d'inertie. Pourquoi changer s'il n'y a aucune raison impérieuse de le faire? Ce n'est pas comme si l'utilisation de primitives en boîte présentait des avantages supplémentaires.
L'autre raison est d'affirmer que ce
null
n'est pas une option valable. Il serait inutile et trompeur de déclarer la somme de deux nombres ou une variable de boucle commeInteger
.Il y a aussi l'aspect performance, alors que la différence de performance n'est pas critique dans de nombreux cas (bien que quand c'est le cas, c'est assez mauvais), personne n'aime écrire du code qui pourrait être écrit aussi facilement et plus rapidement que nous sommes déjà habitué.
la source
À propos, Smalltalk n'a que des objets (pas de primitives), et pourtant ils avaient optimisé leurs petits entiers (en n'utilisant pas tous les 32 bits, seulement 27 ou autres) pour ne pas allouer d'espace de tas, mais simplement utiliser un modèle de bits spécial. De plus, d'autres objets courants (vrai, faux, nul) avaient ici des modèles de bits spéciaux.
Ainsi, au moins sur les JVM 64 bits (avec un espace de noms de pointeur 64 bits), il devrait être possible de ne pas avoir d'objets de type Integer, Character, Byte, Short, Boolean, Float (et small Long) du tout (à part ceux créés par explicite
new ...()
), uniquement des modèles de bits spéciaux, qui pourraient être manipulés par les opérateurs normaux assez efficacement.la source
Je ne peux pas croire que personne n'ait mentionné ce que je pense être la raison la plus importante: "int" est tellement plus facile à taper que "Integer". Je pense que les gens sous-estiment l'importance d'une syntaxe concise. Les performances ne sont pas vraiment une raison pour les éviter, car la plupart du temps, lorsque l'on utilise des nombres, on utilise des index de boucle, et l'incrémentation et la comparaison de ces derniers ne coûtent rien dans une boucle non triviale (que vous utilisiez int ou Integer).
L'autre raison donnée était que vous pouvez obtenir des NPE, mais c'est extrêmement facile à éviter avec les types encadrés (et cela est garanti à éviter tant que vous les initialisez toujours à des valeurs non nulles).
L'autre raison était que (new Long (1000)) == (new Long (1000)) est false, mais c'est juste une autre façon de dire que ".equals" n'a pas de support syntaxique pour les types encadrés (contrairement aux opérateurs <,> , =, etc), nous revenons donc à la raison de la "syntaxe plus simple".
Je pense que l'exemple de boucle non primitive de Steve Yegge illustre très bien mon propos: http://sites.google.com/site/steveyegge2/language-trickery-and-ejb
Pensez à ceci: à quelle fréquence utilisez-vous des types de fonctions dans des langages qui ont une bonne syntaxe pour eux (comme tout langage fonctionnel, python, ruby et même C) par rapport à java où vous devez les simuler à l'aide d'interfaces telles que Runnable et Callable et classes sans nom.
la source
Quelques raisons de ne pas se débarrasser des primitifs:
S'il est éliminé, les anciens programmes ne fonctionneraient même pas.
L'ensemble de la machine virtuelle Java devrait être réécrit pour prendre en charge cette nouvelle chose.
Vous auriez besoin de stocker la valeur et la référence, ce qui utilise plus de mémoire. Si vous avez un grand nombre d'octets, l'utilisation de
byte
's est nettement plus petite que celle deByte
' s.Déclarer
int i
ensuite faire des choses aveci
n'entraînerait aucun problème, mais déclarerInteger i
et faire de même entraînerait un NPE.Considérez ce code:
Ce serait faux. Les opérateurs devraient être surchargés, ce qui entraînerait une réécriture majeure des choses.
Les wrappers d'objets sont nettement plus lents que leurs homologues primitifs.
la source
Les objets sont beaucoup plus lourds que les types primitifs, les types primitifs sont donc beaucoup plus efficaces que les instances de classes wrapper.
Les types primitifs sont très simples: par exemple, un int fait 32 bits et occupe exactement 32 bits en mémoire, et peut être manipulé directement. Un objet Integer est un objet complet, qui (comme tout objet) doit être stocké sur le tas, et n'est accessible que via une référence (pointeur) vers celui-ci. Il occupe très probablement également plus de 32 bits (4 octets) de mémoire.
Cela dit, le fait que Java ait une distinction entre les types primitifs et non primitifs est également un signe de l'âge du langage de programmation Java. Les nouveaux langages de programmation n'ont pas cette distinction; le compilateur d'un tel langage est suffisamment intelligent pour déterminer par lui-même si vous utilisez des valeurs simples ou des objets plus complexes.
Par exemple, dans Scala, il n'y a pas de types primitifs; il existe une classe Int pour les entiers, et un Int est un objet réel (sur lequel vous pouvez utiliser des méthodes, etc.). Lorsque le compilateur compile votre code, il utilise des entiers primitifs dans les coulisses, donc l'utilisation d'un int est tout aussi efficace que l'utilisation d'un int primitif en Java.
la source
En plus de ce que d'autres ont dit, les variables locales primitives ne sont pas allouées à partir du tas, mais à la place sur la pile. Mais les objets sont alloués à partir du tas et doivent donc être récupérés.
la source
Les types primitifs présentent de nombreux avantages:
la source
Il est difficile de savoir quels types d'optimisations sont en cours sous les couvertures.
Pour une utilisation locale, lorsque le compilateur a suffisamment d'informations pour effectuer des optimisations excluant la possibilité de la valeur nulle, je m'attends à ce que les performances soient identiques ou similaires .
Cependant, les tableaux de primitives sont apparemment très différents des collections de primitives encadrées. Cela a du sens étant donné que très peu d'optimisations sont possibles au sein d'une collection.
De plus,
Integer
a une surcharge logique beaucoup plus élevée queint
: maintenant, vous devez vous soucier de savoir siint a = b + c;
une exception est levée ou non .J'utiliserais les primitives autant que possible et me fierais aux méthodes d'usine et à l'auto-boxe pour me donner les types encadrés les plus sémantiquement puissants quand ils sont nécessaires.
la source
En passant, cela ne me dérangerait pas de voir quelque chose comme ça trouver son chemin dans Java.
Où la boucle for incrémente automatiquement loop1 de 0 à 1000 ou
Où la boucle for décrémente automatiquement la boucle1 de 1000 à 0.
la source
Vous devriez demander pourquoi le type de classe / objet est requis
La raison d'avoir un type d'objet est de nous faciliter la vie lorsque nous traitons des collections. Les primitives ne peuvent pas être ajoutées directement à List / Map; vous devez plutôt écrire une classe wrapper. Le type de classes Readymade Integer vous aide ici en plus de nombreuses méthodes utilitaires telles que Integer.pareseInt (str)
la source
Je suis d'accord avec les réponses précédentes, utiliser des objets wrapper primitifs peut être coûteux. Mais, si les performances ne sont pas critiques dans votre application, vous évitez les débordements lors de l'utilisation d'objets. Par exemple:
La valeur de
bigNumber
est -2147483647, et vous vous attendez à ce qu'elle soit 2147483649. C'est un bogue dans le code qui serait corrigé en faisant:Et ce
bigNumber
serait 2147483649. Ce type de bogue est parfois facile à ignorer et peut conduire à des comportements inconnus ou à des vulnérabilités (voir CWE-190 ).Si vous utilisez des objets wrapper, le code équivalent ne sera pas compilé.
Il est donc plus facile d'arrêter ce genre de problèmes en utilisant des objets wrapper primitifs.
Votre question est déjà tellement répondue que je réponds simplement pour ajouter un peu plus d'informations non mentionnées auparavant.
la source
Parce que JAVA effectue toutes les opérations mathématiques dans les types primitifs. Prenons cet exemple:
Ici, les opérations de rappel et unaire plus ne peuvent pas être appliquées au type Integer (Reference), le compilateur effectue le déballage et effectue les opérations.
Alors, assurez-vous du nombre d'opérations de mise en boîte automatique et de déballage effectuées dans le programme java. Depuis, il faut du temps pour effectuer ces opérations.
Généralement, il est préférable de conserver les arguments de type Reference et le résultat de type primitif.
la source
Les types primitifs sont beaucoup plus rapides et nécessitent beaucoup moins de mémoire . Par conséquent, nous préférons peut-être les utiliser.
D'autre part, la spécification actuelle du langage Java ne permet pas l'utilisation de types primitifs dans les types paramétrés (génériques), dans les collections Java ou dans l'API Reflection.
Lorsque notre application a besoin de collections avec un grand nombre d'éléments, nous devrions envisager d'utiliser des tableaux de type le plus «économique» possible.
* Pour des informations détaillées, voir la source: https://www.baeldung.com/java-primitives-vs-objects
la source
Pour être bref: les types primitifs sont plus rapides et nécessitent moins de mémoire que les types en boîte
la source