En Java , il y a des types primitifs pour byte
, short
, int
et long
et la même chose pour float
et double
. Pourquoi est-il nécessaire qu'une personne définisse le nombre d'octets à utiliser pour une valeur primitive? La taille ne pouvait-elle pas être déterminée de manière dynamique en fonction de la taille du nombre transmis?
Il y a 2 raisons auxquelles je peux penser:
- La définition dynamique de la taille des données signifierait qu'elles devraient également pouvoir changer de manière dynamique. Cela pourrait potentiellement causer des problèmes de performances?
- Peut-être que le programmeur ne voudrait pas que quelqu'un puisse utiliser un nombre supérieur à une certaine taille et cela lui permet de le limiter.
Je pense toujours qu'il aurait pu y avoir beaucoup à gagner en utilisant simplement un simple int
et un float
type, y avait-il une raison spécifique pour laquelle Java a décidé de ne pas suivre cette voie?
Réponses:
Comme tant d'aspects de la conception des langues, il s'agit d'un compromis entre l'élégance et la performance (sans parler de l'influence historique des langues antérieures).
Alternatives
Il est certainement possible (et assez simple) de créer un langage de programmation qui n'a qu'un seul type de nombres naturels
nat
. Presque tous les langages de programmation utilisés pour les études universitaires (par exemple PCF, System F) ont ce type de numéro unique, qui est la solution la plus élégante, comme vous l'avez supposé. Mais la conception du langage dans la pratique n'est pas seulement une question d'élégance; nous devons également tenir compte des performances (la mesure dans laquelle les performances sont prises en compte dépend de l'application envisagée du langage). La performance comprend à la fois des contraintes de temps et d'espace.Contraintes d'espace
Laisser le programmeur choisir le nombre d'octets à l'avance peut économiser de l'espace dans les programmes à mémoire limitée. Si tous vos nombres vont être inférieurs à 256, vous pouvez utiliser 8 fois plus de
byte
s quelong
s ou utiliser le stockage enregistré pour des objets plus complexes. Le développeur d'applications Java standard n'a pas à se soucier de ces contraintes, mais elles se présentent.Efficacité
Même si nous ignorons l'espace, nous sommes toujours contraints par le CPU, qui n'a que des instructions qui fonctionnent sur un nombre fixe d'octets (8 octets sur une architecture 64 bits). Cela signifie que même fournir un seul type de 8 octets
long
rendrait l'implémentation du langage beaucoup plus simple que d'avoir un type de nombre naturel illimité, en étant capable de mapper les opérations arithmétiques directement sur une seule instruction CPU sous-jacente. Si vous autorisez le programmeur à utiliser des nombres arbitrairement grands, une seule opération arithmétique doit être mappée sur une séquence d'instructions machine complexes, ce qui ralentirait le programme. C'est le point (1) que vous avez évoqué.Types à virgule flottante
Jusqu'à présent, la discussion n'a porté que sur des entiers. Les types à virgule flottante sont une bête complexe, avec une sémantique et des cas de bord extrêmement subtils. Ainsi, même si nous pourrions remplacer facilement
int
,long
,short
etbyte
avec un seulnat
type, il ne sait pas ce que le type de nombres à virgule flottante même est . Ce ne sont pas de vrais nombres, évidemment, car les vrais nombres ne peuvent pas exister dans un langage de programmation. Ce ne sont pas non plus des nombres tout à fait rationnels (bien qu'il soit simple de créer un type rationnel si vous le souhaitez). Fondamentalement, l'IEEE a décidé d'un moyen d'approcher les nombres réels en quelque sorte, et tous les langages (et programmeurs) y sont restés depuis.Finalement:
Ce n'est pas une raison valable. Premièrement, je ne peux penser à aucune situation dans laquelle les types pourraient naturellement encoder des limites numériques, sans parler des chances astronomiquement faibles que les limites que le programmeur souhaite appliquer correspondraient exactement aux tailles de l'un des types primitifs.
la source
type my_type = int (7, 2343)
?La raison est très simple: l' efficacité . De multiples façons.
Types de données natifs: plus les types de données d'une langue sont proches des types de données sous-jacents du matériel, plus la langue est considérée comme efficace. (Pas dans le sens où vos programmes seront nécessairement efficaces, mais dans le sens où vous pouvez, si vous savez vraiment ce que vous faites, écrire du code qui fonctionnera aussi efficacement que le matériel peut l'exécuter.) Les types de données proposés par Java correspondent aux octets, mots, mots doubles et quadruples du matériel le plus populaire sur le marché. C'est la façon la plus efficace de procéder.
Surcharge injustifiée sur les systèmes 32 bits: si la décision avait été prise de tout mapper sur une longueur fixe de 64 bits, cela aurait imposé une énorme pénalité aux architectures 32 bits qui nécessitent beaucoup plus de cycles d'horloge pour effectuer un 64- opération de bits qu'une opération de 32 bits.
Perte de mémoire: il y a beaucoup de matériel qui n'est pas trop pointilleux sur l'alignement de la mémoire (les architectures Intel x86 et x64 en sont des exemples), donc un tableau de 100 octets sur ce matériel ne peut occuper que 100 octets de mémoire. Cependant, si vous n'avez plus d'octet et que vous devez utiliser un long à la place, le même tableau occupera un ordre de grandeur de plus de mémoire. Et les tableaux d'octets sont très courants.
Calcul de la taille des nombres: votre idée de déterminer la taille d'un entier de manière dynamique en fonction de la taille du nombre transmis était trop simpliste; il n'y a pas un seul point de «passer» un nombre; le calcul de la taille d'un nombre doit être effectué au moment de l'exécution, à chaque opération pouvant nécessiter un résultat d'une plus grande taille: chaque fois que vous incrémentez un nombre, chaque fois que vous ajoutez deux nombres, chaque fois que vous multipliez deux numéros, etc.
Opérations sur des nombres de tailles différentes: Par la suite, avoir des nombres de tailles potentiellement différentes flottant dans la mémoire compliquerait toutes les opérations: même pour comparer simplement deux nombres, le runtime devrait d'abord vérifier si les deux nombres à comparer sont les mêmes sinon, redimensionnez le plus petit pour qu'il corresponde à la taille du plus grand.
Opérations qui nécessitent des tailles d'opérande spécifiques: Certaines opérations au niveau du bit reposent sur l'entier ayant une taille spécifique. N'ayant pas de taille spécifique prédéterminée, ces opérations devraient être émulées.
Frais généraux du polymorphisme: la modification de la taille d'un nombre au moment de l'exécution signifie essentiellement qu'il doit être polymorphe. Cela signifie à son tour qu'il ne peut pas être une primitive de taille fixe allouée sur la pile, il doit être un objet, alloué sur le tas. C'est terriblement inefficace. (Relisez le numéro 1 ci-dessus.)
la source
Pour éviter de répéter les points qui ont été discutés dans d'autres réponses, j'essaierai plutôt de décrire plusieurs perspectives.
Du point de vue de la conception de la langue
Raisons historiques
Ceci est déjà discuté dans l'article Wikipedia sur l'histoire de Java, et est également brièvement discuté dans la réponse de Marco13 .
Je voudrais souligner que:
Raisons d'efficacité
Quand l'efficacité est-elle importante?
Efficacité du stockage (en mémoire ou sur disque)
Efficacité d'exécution (au sein du CPU, ou entre CPU et mémoire)
La nécessité pour les langages de programmation de fournir une abstraction pour les petits entiers, même s'ils sont limités à des contextes spécifiques
L'interopérabilité
char
tableau de taille 256. (Exemple.)BitConverter
) pour aider à compresser et décompresser des entiers étroits en flux binaires et en octets.Gestion des chaînes
Gestion du format de fichier
Souhaitabilité, qualité du logiciel et responsabilité du programmeur
Considérez le scénario suivant.
Souvent, les logiciels qui peuvent évoluer en toute sécurité sur plusieurs ordres de grandeur doivent être conçus à cette fin, avec une complexité croissante. Il ne vient pas automatiquement même si le problème de dépassement d'entier est éliminé. Cela revient à un cercle complet répondant à la perspective de conception de langage: souvent, un logiciel qui refuse d'effectuer un travail lorsqu'un débordement d'entier involontaire se produit (en lançant une erreur ou une exception) est meilleur qu'un logiciel qui se conforme automatiquement à des opérations astronomiquement importantes.
Cela signifie la perspective du PO,
n'est pas correcte. Le programmeur doit être autorisé, et parfois obligé, à spécifier la magnitude maximale qu'une valeur entière peut prendre, dans les parties critiques du logiciel. Comme le souligne la réponse de gardenhead , les limites naturelles imposées par les types primitifs ne sont pas utiles à cette fin; le langage doit fournir aux programmeurs des moyens de déclarer des amplitudes et d'appliquer ces limites.
la source
Tout vient du matériel.
Un octet est la plus petite unité de mémoire adressable sur la plupart du matériel.
Chaque type que vous venez de mentionner est construit à partir de plusieurs multiples d'octets.
Un octet fait 8 bits. Avec cela, vous pouvez exprimer 8 booléens mais vous ne pouvez pas rechercher un seul à la fois. Vous vous adressez à 1, vous vous adressez aux 8.
Auparavant, c'était aussi simple que cela, mais nous sommes passés d'un bus 8 bits à un bus 16, 32 et maintenant 64 bits.
Ce qui signifie que même si nous pouvons encore adresser au niveau des octets, nous ne pouvons plus récupérer un seul octet de la mémoire sans obtenir ses octets voisins.
Face à ce matériel, les concepteurs de langage ont choisi de nous permettre de choisir des types qui nous permettaient de choisir des types adaptés au matériel.
Vous pouvez prétendre qu'un tel détail peut et doit être résumé, en particulier dans un langage qui vise à s'exécuter sur n'importe quel matériel. Cela aurait des problèmes de performances cachés, mais vous avez peut-être raison. Cela ne s'est tout simplement pas produit de cette façon.
Java essaie en fait de le faire. Les octets sont automatiquement promus en Ints. Un fait qui vous rendra fou la première fois que vous essayez de faire un travail de changement de bit sérieux.
Alors pourquoi ça n'a pas bien fonctionné?
Le grand argument de vente de Java remonte à l'époque où vous pouviez vous asseoir avec un bon algorithme C connu, le taper en Java, et avec des ajustements mineurs, cela fonctionnerait. Et C est très proche du matériel.
Garder cette taille active et abstraite des types intégraux ne fonctionnait tout simplement pas ensemble.
Alors ils auraient pu. Ils ne l'ont tout simplement pas fait.
C'est une pensée valable. Il existe des méthodes pour ce faire. La fonction de serrage pour un. Un langage pourrait aller jusqu'à créer des limites arbitraires dans leurs types. Et lorsque ces limites sont connues au moment de la compilation, cela permettrait d'optimiser la façon dont ces nombres sont stockés.
Java n'est tout simplement pas ce langage.
la source
Probablement, une raison importante de la raison pour laquelle ces types existent en Java est simple et malheureusement non technique:
C et C ++ avaient également ces types!
Bien qu'il soit difficile de prouver que c'est la raison, il existe au moins quelques preuves solides: la spécification du langage Oak (version 0.2) contient le passage suivant:
La question pourrait donc se résumer à:
Pourquoi court, int et long ont-ils été inventés en C?
Je ne sais pas si la réponse à la lettre est satisfaisante dans le contexte de la question qui a été posée ici. Mais en combinaison avec les autres réponses ici, il pourrait devenir clair qu'il peut être avantageux d'avoir ces types (indépendamment du fait que leur existence en Java soit seulement un héritage de C / C ++).
Les raisons les plus importantes auxquelles je peux penser sont
Un octet est la plus petite unité de mémoire adressable (comme CandiedOrange l'a déjà mentionné). A
byte
est le bloc élémentaire de construction des données, qui peut être lu à partir d'un fichier ou sur le réseau. Certains de représentation explicite devrait exister (et il existe dans la plupart des langues, même quand il est parfois déguisé).Il est vrai que, dans la pratique, il serait logique de représenter tous les champs et les variables locales à l'aide d'un seul type, et d'appeler ce type
int
. Il y a une question connexe à ce sujet sur stackoverflow: pourquoi l'API Java utilise-t-elle int au lieu de short ou byte? . Comme je l'ai mentionné dans ma réponse, une justification pour avoir les types plus petits (byte
etshort
) est que vous pouvez créer des tableaux de ces types: Java a une représentation des tableaux qui est encore plutôt "proche du matériel". Contrairement à d'autres langages (et contrairement aux tableaux d'objets, comme unInteger[n]
tableau), unint[n]
tableau n'est pas une collection de références où les valeurs sont dispersées dans le tas. Au lieu de cela, il seraen pratique, un bloc d'n*4
octets consécutif - un bloc de mémoire de taille et de disposition de données connues. Lorsque vous avez le choix de stocker 1000 octets dans une collection d'objets de valeur entière de taille arbitraire, ou dans unbyte[1000]
(qui prend 1000 octets), ce dernier peut en effet économiser de la mémoire. (Certains autres avantages peuvent être plus subtils et ne deviennent évidents que lors de l'interfaçage de Java avec des bibliothèques natives)Concernant les points que vous avez spécifiquement interrogés:
Il serait probablement possible de définir dynamiquement la taille des variables, si l'on envisageait de concevoir un tout nouveau langage de programmation à partir de zéro. Je ne suis pas un expert en construction de compilateurs, mais je pense qu'il serait difficile de gérer raisonnablement les collections de types à changement dynamique - en particulier, lorsque vous avez un langage fortement typé. Donc, cela se résumerait probablement à tous les nombres stockés dans un "type de données numériques de précision arbitraire", ce qui aurait certainement des répercussions sur les performances. Bien sûr, il existe des langages de programmation qui sont fortement typés et / ou proposent des types de nombres de taille arbitraire, mais je ne pense pas qu'il existe un véritable langage de programmation à usage général qui soit allé de cette façon.
Notes annexes:
Vous vous êtes peut-être posé des questions sur le
unsigned
modificateur mentionné dans la spécification Oak. En fait, il contient également une remarque: "unsigned
n'est pas encore implémenté; il pourrait ne jamais l'être." . Et ils avaient raison.En plus de vous demander pourquoi C / C ++ avait ces différents types d'entiers, vous vous demandez peut-être pourquoi ils les ont gâchés si horriblement que vous ne savez jamais combien de bits il y
int
a. Les justifications sont généralement liées à la performance et peuvent être consultées ailleurs.la source
Cela montre certainement que vous n'avez pas encore appris les performances et les architectures.
Ignorer l'importance de la taille des données affecte toujours les performances, vous devez utiliser autant de ressources que nécessaire, mais pas plus, toujours!
C'est la différence entre un programme ou un système qui fait des choses vraiment simples et qui est incroyablement inefficace, qui nécessite beaucoup de ressources et qui rend l'utilisation de ce système très coûteuse; ou un système qui fait beaucoup, mais qui tourne plus vite que les autres et qui est vraiment bon marché.
la source
Il y a quelques bonnes raisons
(1) alors que le stockage d'un octet variable vers un long est insignifiant, le stockage de millions dans un tableau est très important.
(2) L'arithmétique «native du matériel» basée sur des tailles entières particulières peut être beaucoup plus efficace, et pour certains algorithmes sur certaines plates-formes, cela peut être important.
la source