web-dev-qa-db-fra.com

Pourquoi l'API Java utilise int au lieu de short ou byte?

Pourquoi l'API Java Java utilise int, alors que short ou même byte serait suffisant?

Exemple: le DAY_OF_WEEK champ dans la classe Calendar utilise int.

Si la différence est trop minime, pourquoi ces types de données (short, int) existent-ils?

134
Willi Mentzel

Certaines des raisons ont déjà été signalées. Par exemple, le fait que "... (Presque) Toutes les opérations sur octet, short promeuvent ces primitives en int" . Cependant, la prochaine question évidente serait: [~ # ~] pourquoi [~ # ~] ces types sont-ils promus en int?

Donc, pour aller plus loin: la réponse peut simplement être liée au Java Virtual Machine Instruction Set. Comme résumé dans le Tableau dans le Java = Spécification de la machine virtuelle , toutes les opérations arithmétiques intégrales , comme l'ajout, la division et d'autres, sont uniquement disponibles pour le type int et le type long, et pas pour les types plus petits.

(Un côté: les types plus petits (byte et short) ne sont essentiellement destinés qu'aux tableaux. Un tableau comme new byte[1000] prendra 1 000 octets et un tableau comme new int[1000] prendra 4 000 octets)

Maintenant, bien sûr, on pourrait dire que "... la prochaine question évidente serait: [~ # ~] pourquoi [~ # ~] ces instructions sont-elles uniquement proposées pour int (et long)? ".

Une raison est mentionnée dans la spécification JVM mentionnée ci-dessus:

Si chaque instruction tapée prenait en charge tous les types de données d'exécution de la machine virtuelle Java Virtual Machine), il y aurait plus d'instructions que ce qui pourrait être représenté dans un octet

De plus, la machine virtuelle Java Virtual Machine peut être considérée comme l'abstraction d'un vrai processeur. Et l'introduction de Arithmetic Logic Unit pour les types plus petits ne vaut pas la peine: elle aurait besoin de transistors supplémentaires, mais il ne pouvait toujours exécuter qu'un seul ajout dans un cycle d'horloge. L'architecture dominante lors de la conception de la JVM était de 32 bits, juste pour un 32 bits int. (Les opérations qui impliquent un 64 bits long value sont implémentées comme un cas spécial).

(Remarque: Le dernier paragraphe est un peu trop simplifié, compte tenu de la vectorisation possible, etc., mais devrait donner l'idée de base sans plonger trop profondément dans les sujets de conception du processeur)


EDIT: Un court addendum, se concentrant sur l'exemple de la question, mais dans un sens plus général: on pourrait également se demander s'il ne serait pas avantageux de stocker champs en utilisant les types plus petits. Par exemple, on pourrait penser que la mémoire pourrait être sauvegardée en stockant Calendar.DAY_OF_WEEK en tant que byte. Mais ici, le Java Class File Format entre en jeu: tous les champs dans un fichier de classe occupent au moins un "slot", qui a la taille d'un int (32 bits). (Les champs "larges", double et long, occupent deux emplacements). Ainsi, déclarer explicitement un champ comme short ou byte ne sauverait pas non plus de mémoire.

164
Marco13

(Presque) Toutes les opérations sur byte, short les promouvront en int, par exemple, vous ne pouvez pas écrire:

short x = 1;
short y = 2;

short z = x + y; //error

Les arithmétiques sont plus faciles et directes lors de l'utilisation de int, pas besoin de lancer.

En termes d'espace, cela fait très peu de différence. byte et short compliqueraient les choses, je ne pense pas que cette micro-optimisation en vaille la peine puisque nous parlons d'un nombre fixe de variables.

byte est pertinent et utile lorsque vous programmez pour des périphériques intégrés ou que vous traitez des fichiers/réseaux. De plus, ces primitives sont limitées, que faire si les calculs pourraient dépasser leurs limites à l'avenir? Essayez de penser à une extension pour la classe Calendar qui pourrait faire évoluer des nombres plus importants.

Notez également que dans les processeurs 64 bits, les sections locales seront enregistrées dans les registres et n'utiliseront aucune ressource, donc l'utilisation de int, short et d'autres primitives ne fera aucune différence. . De plus, de nombreuses implémentations Java alignent les variables* (et objets).


* byte et short occupent le même espace que int si ce sont des variables locales, classe ou même - instance variables. Pourquoi? Parce que dans (la plupart) des systèmes informatiques, les adresses des variables sont alignées, donc par exemple si vous utilisez un seul octet, vous vous retrouverez en fait avec deux octets - un pour la variable elle-même et un autre pour le remplissage .

En revanche, dans les tableaux, byte prend 1 octet, short prend 2 octets et int prend quatre octets, car dans les tableaux, seul le début et peut-être la fin de celui-ci doit être aligné. Cela fera une différence dans le cas où vous souhaitez utiliser, par exemple, System.arraycopy(), alors vous remarquerez vraiment une différence de performances.

39
Maroun

Parce que les opérations arithmétiques sont plus faciles lors de l'utilisation d'entiers par rapport aux courts métrages. Supposons que les constantes ont bien été modélisées par des valeurs short. Ensuite, vous devrez utiliser l'API de cette manière:

short month = Calendar.JUNE;
month = month + (short) 1; // is july

Remarquez le casting explicite. Les valeurs courtes sont implicitement promues en valeurs int lorsqu'elles sont utilisées dans des opérations arithmétiques. (Sur la pile d'opérandes, les courts métrages sont même exprimés en ints.) Ce serait assez lourd à utiliser, c'est pourquoi les valeurs int sont souvent préférées pour les constantes.

Par rapport à cela, le gain en efficacité de stockage est minime car il n'existe qu'un nombre fixe de telles constantes. Nous parlons de 40 constantes. Changer leur stockage de int à short vous protégerait 40 * 16 bit = 80 byte. Voir cette réponse pour plus de référence.

7

Si vous avez utilisé la philosophie selon laquelle les constantes intégrales sont stockées dans le plus petit type auquel elles correspondent, alors Java aurait un grave problème: chaque fois que les programmeurs écrivent du code à l'aide de constantes intégrales, ils doivent faire très attention à leur code pour vérifier si le type des constantes est important, et si oui, recherchez le type dans la documentation et/ou effectuez les conversions de type nécessaires.

Alors maintenant que nous avons décrit un problème grave, quels avantages pourriez-vous espérer obtenir avec cette philosophie? Je ne serais pas surpris si l'effet niquement observable à l'exécution de ce changement était le type que vous obtenez lorsque vous recherchez la constante via la réflexion. (et, bien sûr, quelles que soient les erreurs introduites par les programmeurs paresseux/involontaires ne tenant pas correctement compte des types de constantes)

Peser le pour et le contre est très simple: c'est une mauvaise philosophie.

5
Hurkyl

La complexité de conception d'une machine virtuelle est fonction du nombre d'opérations qu'elle peut effectuer. Il est plus facile d'avoir quatre implémentations d'une instruction comme "multiplier" - une pour un entier 32 bits, un entier 64 bits, une virgule flottante 32 bits et une virgule flottante 64 bits - que d'avoir, en plus à ce qui précède, des versions pour les petits types numériques également. Une question de conception plus intéressante est pourquoi il devrait y avoir quatre types, plutôt que moins (effectuer tous les calculs entiers avec des entiers 64 bits et/ou faire tous les calculs en virgule flottante avec des valeurs en virgule flottante 64 bits). La raison de l'utilisation d'entiers 32 bits est que Java devait s'exécuter sur de nombreuses plates-formes où les types 32 bits pouvaient être traités aussi rapidement que les types 16 bits ou 8 bits, mais les opérations sur les types 64 bits seraient sensiblement plus lentes. Même sur les plates-formes où les types 16 bits seraient plus rapides à utiliser, le coût supplémentaire de travail avec des quantités 32 bits serait compensé par la simplicité offerte par niquement ayant des types 32 bits.

Quant aux calculs en virgule flottante sur des valeurs 32 bits, les avantages sont un peu moins clairs. Il existe des plates-formes où un calcul comme float a=b+c+d; pourrait être exécuté plus rapidement en convertissant tous les opérandes en un type de précision supérieure, en les ajoutant, puis en reconvertissant le résultat en un nombre à virgule flottante 32 bits pour le stockage. Il existe d'autres plates-formes où il serait plus efficace d'effectuer tous les calculs à l'aide de valeurs à virgule flottante 32 bits. Les créateurs de Java ont décidé que toutes les plates-formes devraient être obligées de faire les choses de la même manière, et qu'elles devraient privilégier les plates-formes matérielles pour lesquelles les calculs en virgule flottante 32 bits sont plus rapides que les plus longs, même si ce PC a gravement dégradé à la fois la vitesse et la précision des calculs à virgule flottante sur un PC typique, ainsi que sur de nombreuses machines sans unités à virgule flottante. Notez, btw, qu'en fonction des valeurs de b, c et d, en utilisant des calculs intermédiaires de plus grande précision lors du calcul d'expressions telles que float a=b+c+d; donnera parfois des résultats beaucoup plus précis que ceux obtenus avec tous les opérandes intermédiaires calculés avec une précision de float, mais produira parfois une valeur un peu moins précise. Dans tous les cas, Sun a décidé que tout devait être fait de la même manière, et ils ont opté pour l'utilisation de valeurs float de précision minimale.

Notez que les principaux avantages des types de données plus petits deviennent évidents lorsqu'un grand nombre d'entre eux sont stockés ensemble dans un tableau; même s'il n'y avait aucun avantage à avoir des variables individuelles de types inférieurs à 64 bits, il vaut la peine d'avoir des tableaux qui peuvent stocker des valeurs plus petites de manière plus compacte; avoir une variable locale soit un byte plutôt qu'un long économise sept octets; ayant un tableau de 1 000 000 de numéros, chaque numéro est un byte plutôt qu'un long ondule 7 000 000 octets. Étant donné que chaque type de tableau ne doit prendre en charge que quelques opérations (notamment lire un élément, stocker un élément, copier une plage d'éléments dans un tableau ou copier une plage d'éléments d'un tableau à un autre), la complexité supplémentaire d'avoir plus les types de tableau ne sont pas aussi graves que la complexité d'avoir plus de types de valeurs numériques discrètes directement utilisables.

4
supercat

En fait, il y aurait un petit avantage. Si tu as un

class MyTimeAndDayOfWeek {
    byte dayOfWeek;
    byte hour;
    byte minute;
    byte second;
}

puis sur une machine virtuelle Java typique, elle a besoin d'autant d'espace qu'une classe contenant un seul int. La consommation de mémoire est arrondie au multiple de 8 ou 16 octets suivant (IIRC, c'est configurable), donc les cas où il y a une réelle économie sont plutôt rares.

Cette classe serait légèrement plus facile à utiliser si les méthodes Calendar correspondantes renvoyaient un byte. Mais il n'y a pas de telles méthodes Calendar, seulement get(int) qui doit retourner un int à cause d'autres champs. Chaque opération sur des types plus petits se transforme en int, vous avez donc besoin de beaucoup de transtypage.

Très probablement, vous abandonnerez et basculerez vers un int ou vous écrivez des setters comme

void setDayOfWeek(int dayOfWeek) {
    this.dayOfWeek = checkedCastToByte(dayOfWeek);
}

De toute façon, le type de DAY_OF_WEEK N'a pas d'importance.

2
maaartinus