web-dev-qa-db-fra.com

Pourquoi les gens utilisent-ils encore des types primitifs en Java?

Depuis Java 5, nous avons eu la boxe/unboxing de types primitifs de sorte que int soit encapsulé pour être Java.lang.Integer, et ainsi de suite.

Je vois beaucoup de nouveaux Java projets récemment (qui ont définitivement besoin de -) à compter de 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 dispose de quelques méthodes auxiliaires pour convertir en long values ​​et al.

Pourquoi certains encore utilisent-ils des types primitifs en Java? Y at-il un avantage tangible?

154
Naftuli Kay

Dans Joshua Bloch Effective Java, élément 5: "Évitez de créer des objets inutiles", il publie l'exemple de code suivant:

public static void main(String[] args) {
    Long sum = 0L; // uses Long, not long
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}

et cela prend 43 secondes pour courir. Prendre le long dans la primitive la réduit à 6,8 secondes ... Si c'est une indication, pourquoi utiliser des primitives.

L'absence d'égalité des valeurs natives est également un problème (.equals() est assez détaillé comparé à ==)

pour biziclop:

class Biziclop {

    public static void main(String[] args) {
        System.out.println(new Integer(5) == new Integer(5));
        System.out.println(new Integer(500) == new Integer(500));

        System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
    }
}

Résulte en:

false
false
true
false

MODIFIER  Pourquoi (3) renvoie true et (4) renvoie false?

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 machine virtuelle, ils renvoient donc le même objet pour ceux-ci. Au-delà de cette plage, ils ne sont pas mis en cache. Un nouvel objet est donc créé. Pour rendre les choses plus compliquées, le JLS exige que au moins = 256 poids volants soient mis en cache. Les développeurs JVM peuvent en ajouter d’autres s’ils le souhaitent, ce qui signifie que cela pourrait s’exécuter sur un système dans lequel les 1024 les plus proches sont mis en cache et que tous retournent la valeur true ... #awkward

377
corsiKa

La box automatique peut rendre difficile la détection des NPE

Integer in = null;
...
...
int i = in; // NPE at runtime

Dans la plupart des cas, l'attribution nulle à in est beaucoup moins évidente que précédemment.

80
Heiko Rupp

Les types Boxed ont de moins bonnes performances et nécessitent plus de mémoire.

40
Orbit

Types primitifs:

int x = 1000;
int y = 1000;

Maintenant, évaluez:

x == y

C'est true. Pas vraiment surprenant. Maintenant, essayez les types en boîte:

Integer x = 1000;
Integer y = 1000;

Maintenant, évaluez:

x == y

C'est false. Probablement. Dépend du runtime. Cette raison est-elle suffisante?

40
Daniel Earwicker

Outre les problèmes de performances et de mémoire, j'aimerais aborder un autre problème: l'interface List serait cassée sans int.
Le problème est la méthode surchargée remove() ( remove(int) vs remove(Object) ) . remove(Integer) résolvera toujours d'appeler ce dernier, vous ne pourrez 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:

final int i = 42;
final List<Integer> list = new ArrayList<Integer>();
list.add(i); // add(Object)
list.remove(i); // remove(int) - Ouch!
35
xehpuk

Pouvez-vous vraiment imaginer un

  for (int i=0; i<10000; i++) {
      do something
  }

boucle avec Java.lang.Integer à la place? Un objet Java.lang.Integer est immuable. Ainsi, chaque incrément autour de la boucle créerait un nouvel objet Java sur le tas, au lieu d’incrémenter simplement l’int sur la pile avec une seule instruction JVM. la performance serait diabolique.

Je ne suis vraiment pas d'accord qu'il est beaucoup plus pratique d'utiliser Java.lang.Integer que int. Au contraire. La sélection automatique signifie que vous pouvez utiliser int là où vous seriez sinon obligé d'utiliser Integer, et le compilateur Java se charge de l'insertion du code pour créer le nouvel objet Integer à votre place. Autoboxing consiste à autoriser vous devez utiliser un int où un entier est attendu, avec le compilateur insérant la construction d'objet appropriée. Elle ne supprime ni ne réduit en rien le besoin de l'int. En premier lieu, l'autoboxing vous permet d'obtenir le meilleur des deux mondes. Entier créé pour vous automatiquement lorsque vous avez besoin d'un objet basé sur un tas Java) et que vous obtenez la vitesse et l'efficacité d'un int lorsque vous effectuez des calculs arithmétiques et locaux.

27
Tom Quarendon

Les types primitifs sont beaucoup plus rapides:

int i;
i++;

Entier (tous les nombres et aussi une chaîne) est un type immuable: une fois créé, il ne peut plus être modifié. Si i était Integer, alors i++ créerait un nouvel objet Integer - beaucoup plus coûteux en termes de mémoire et de processeur.

19
Peter Knego

D'abord et avant tout, l'habitude. Si vous avez codé Java pendant huit ans, vous accumulez une inertie considérable. Pourquoi changer s'il n'y a aucune raison impérieuse de le faire? Ce n'est pas comme si les primitives en boîte étaient livrées avec des avantages supplémentaires.

L'autre raison est d'affirmer que null n'est pas une option valide. Il serait inutile et trompeur de déclarer la somme de deux nombres ou d'une variable de boucle sous la forme Integer.

Il y a aussi l'aspect performance, bien que la différence de performance ne soit pas critique dans de nombreux cas (bien que ce soit vraiment mauvais), personne n'aime écrire du code qui pourrait être écrit aussi facilement de manière plus rapide. habitué.

16
biziclop

Soit dit en passant, Smalltalk n'a que des objets (pas de primitives) et pourtant, ils avaient optimisé leurs petits entiers (n'utilisant pas tous les 32 bits, mais seulement 27 ou plus) pour ne pas allouer d'espace de segment de mémoire, mais simplement pour utiliser un modèle de bits spécial. D'autres objets communs (true, false, null) ont également des modèles de bits spéciaux.

Ainsi, au moins sur les machines JVM 64 bits (avec un espace de nom de pointeur 64 bits), il devrait être possible de ne pas avoir d’objets Integer, Character, Byte, Short, Boolean, Float (et small Long) (à part ces objets créés). par explicite new ...()), seuls les modèles de bits spéciaux, qui pourraient être manipulés par les opérateurs normaux de manière assez efficace.

12
Paŭlo Ebermann

Je ne peux pas croire que personne n'ait mentionné ce qui me semble être la raison la plus importante: "int" est tellement, 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, quand on utilise des nombres, on utilise des index de boucle, et incrémenter et comparer ces coûts ne coûte rien dans une boucle non triviale (que vous utilisiez int ou Integer).

L'autre raison invoquée est que vous pouvez obtenir des NPE, mais il est extrêmement facile d'éviter les types encadrés (et il est garanti de l'éviter tant que vous les initialisez toujours avec des valeurs non NULL).

L’autre raison était que (new long (1000)) == (new long (1000)) est faux, mais c’est une autre façon de dire que ".equals" n’a aucun support syntaxique pour les types encadrés (contrairement aux opérateurs <,> , =, etc), on revient donc à la raison "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

Réfléchissez à ceci: combien de fois utilisez-vous des types de fonction dans des langages qui ont une bonne syntaxe (comme tout langage fonctionnel, python, Ruby et même C) par rapport à Java où vous devez simuler en utilisant des interfaces telles que les classes Runnable et Callable et sans nom.

9
CromTheDestroyer

Quelques raisons de ne pas se débarrasser des primitifs:

  • Compatibilité descendante.

S'il est éliminé, tous les anciens programmes ne fonctionneraient même pas.

  • Réécriture de la JVM.

L'ensemble de la machine virtuelle Java devra être réécrit pour prendre en charge cette nouvelle fonctionnalité.

  • Empreinte mémoire plus importante.

Vous devez stocker la valeur et la référence, qui utilisent plus de mémoire. Si vous avez un grand nombre d'octets, l'utilisation de byte est nettement plus petite que celle de Byte.

  • Problème de pointeur nul.

Déclarer int i alors faire des choses avec i n’aurait aucun problème, mais déclarer Integer i et faire la même chose aboutirait à un NPE.

  • Problèmes d'égalité.

Considérons ce code:

Integer i1 = 5;
Integer i2 = 5;

i1 == i2; // Currently would be false.

Serait faux. Les opérateurs devraient être surchargés, ce qui entraînerait une réécriture majeure des éléments.

  • Lent

Les wrappers d'objet sont nettement plus lents que leurs homologues primitifs.

8
Anubian Noob

Les objets étant beaucoup plus lourds que les types primitifs, les types primitifs sont beaucoup plus efficaces que les instances de classes wrapper.

Les types primitifs sont très simples: par exemple, un int a 32 bits, occupe exactement 32 bits en mémoire et peut être manipulé directement. Un objet Integer est un objet complet qui (comme n'importe quel objet) doit être stocké sur le tas et ne peut être accédé que via une référence (pointeur). Il est fort probable qu'il utilise également plus de 32 bits (4 octets) de mémoire.

Cela dit, le fait que Java distingue les types primitifs des types non primitifs est également un signe d'âge du langage de programmation Java. Nouveaux langages de programmation ne faites 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 inits primitifs en coulisse, l’utilisation d’un int est tout aussi efficace que l’utilisation d’un int primitif en Java.

7
u449355

En plus de ce que d'autres ont dit, les variables locales primitives ne sont pas allouées à partir du tas, mais sur la pile. Mais les objets sont alloués à partir du tas et doivent donc être ramassés.

7
Eddie

Les types primitifs présentent de nombreux avantages:

  • Code plus simple à écrire
  • Les performances sont meilleures puisque vous n'instanciez pas d'objet pour la variable
  • Comme ils ne représentent pas une référence à un objet, il n'est pas nécessaire de rechercher les valeurs NULL.
  • Utilisez des types primitifs, sauf si vous devez tirer parti des fonctionnalités de boxe.
5
Adriana
int loops = 100000000;

long start = System.currentTimeMillis();
for (Long l = new Long(0); l<loops;l++) {
    //System.out.println("Long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start));

start = System.currentTimeMillis();
for (long l = 0; l<loops;l++) {
    //System.out.println("long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start));

Millisecondes parcourues pour boucler '100000000' fois autour de Long: 468

Millisecondes parcourues pour boucler '100000000' fois: 31

Sur une note de côté, je voudrais bien voir quelque chose comme ceci trouver sa place dans Java.

Integer loop1 = new Integer(0);
for (loop1.lessThan(1000)) {
   ...
}

Où la boucle for incrémente automatiquement loop1 de 0 à 1000 ou

Integer loop1 = new Integer(1000);
for (loop1.greaterThan(0)) {
   ...
}

Où la boucle for décrémente automatiquement loop1 1000 à 0.

5
Keith

Il est difficile de savoir quel type d’optimisation est en cours.

Pour une utilisation locale, lorsque le compilateur dispose de suffisamment d'informations pour effectuer des optimisations excluant la possibilité de la valeur NULL, , les performances sont identiques ou similaires .

Cependant, les tableaux de primitives sont apparemment très différents à partir de collections de primitives en boîte. Cela a du sens étant donné que très peu d'optimisations sont possibles au sein d'une collection.

De plus, Integer a une charge logique beaucoup plus élevée par rapport à int: vous devez maintenant vous demander si ou pas int a = b + c; lève une exception.

J'utilisais les primitives autant que possible et m'appuyais sur les méthodes d'usine et l'autoboxing pour me donner les types boxed sémantiquement plus puissants au besoin.

5
Matthew Willis

Je suis d'accord avec les réponses précédentes, utiliser des objets wrapper de primitives peut être coûteux. Toutefois, si les performances ne sont pas critiques dans votre application, vous évitez les débordements lors de l'utilisation d'objets. Par exemple:

long bigNumber = Integer.MAX_VALUE + 2;

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é de la manière suivante:

long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now (it is '2L').

Et bigNumber correspondrait à 2147483649. Ces types de bogues sont parfois faciles à manquer et peuvent conduire à un comportement inconnu ou à des vulnérabilités (voir CWE-19 ).

Si vous utilisez des objets wrapper, le code équivalent ne sera pas compilé.

Long bigNumber = Integer.MAX_VALUE + 2; // Not compiling

Il est donc plus facile d'arrêter ce type de problèmes en utilisant des objets wrapper de primitives.

Votre question a déjà déjà reçu une réponse, et je réponds simplement pour ajouter un peu plus d’informations qui n’ont pas été mentionnées auparavant.

2
Fernando Garcia
  1. Vous avez besoin de primitives pour faire des opérations mathématiques
  2. Les primitives prennent moins de mémoire comme indiqué ci-dessus et sont plus performantes

Vous devriez demander pourquoi le type de classe/objet est requis

La raison pour laquelle le type d'objet est de nous faciliter la vie lorsque nous traitons avec 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 outre, il a de nombreuses méthodes utilitaires comme Integer.pareseInt (str).

2
Prabakaran

Les types primitifs sont beaucoup plus rapides et nécessitent beaucoup moins de mémoire . Par conséquent, nous pourrions préférer les utiliser.

Par contre, la spécification courante 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 le API.

Lorsque notre application a besoin de collections contenant un grand nombre d’éléments, nous vous recommandons d’utiliser des tableaux de type le plus "économique" possible, comme il est illustré dans le graphique ci-dessus.

* Pour des informations détaillées, voir la source: https://www.baeldung.com/Java-primitives-vs-objects

0
Arun Joshla

Parce que Java effectue toutes les opérations mathématiques dans les types primitifs. Prenons cet exemple:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li)
        if (i % 2 == 0)
            sum += i;
        return sum;
}

Ici, les opérations de rappel et unaires plus ne peuvent pas être appliquées sur le type Integer (Reference), le compilateur effectue un unboxing et effectue les opérations.

Assurez-vous donc combien d'opérations de liste déroulante et de liste déroulante automatiques se produisent dans le programme Java. Depuis, l'exécution de cette opération prend du temps.

En règle générale, il est préférable de conserver les arguments de type Référence et le résultat de type primitif.

0
user3462649