Почему Java API использует int вместо short или byte?

почему Java API использует int, когда short или даже byte будет достаточно?

Пример:DAY_OF_WEEK

6 ответов


некоторые из причин уже были указаны. Например, тот факт, что "...(Почти) все операции над byte, short будут продвигать эти примитивы до int". Однако очевидным следующим вопросом будет:почему эти типы повышены до int?

Итак, чтобы пойти на один уровень глубже: ответ может быть просто связан с набором инструкций виртуальной машины Java. Как резюмируется в таблица в виртуальном Java Спецификация Машины, все интегральные арифметические операции, как сложение, деление и другие, доступны только для типа int тип long и не для небольших типов.

(в сторону: меньшие типы (byte и short) в основном предназначены только для массивы. Ан массив как new byte[1000] займет 1000 байт, и массив, как new int[1000] примет 4000 байт)

теперь, конечно, можно сказать, что "...следующий очевидный вопрос:--23-->почему эти инструкции, предлагаются только для intlong)?".

одна из причин упоминается в спецификации JVM, упомянутой выше:

если бы каждая типизированная инструкция поддерживала все типы данных времени выполнения виртуальной машины Java, было бы больше инструкций, чем может быть представлено в байт

кроме того, виртуальную машину Java можно рассматривать как абстракцию реального процессора. И представляя посвященный Арифметический Логический Блок для меньших типов не стоило бы усилий: ему понадобились бы дополнительные транзисторы, но он все равно мог бы выполнить только одно дополнение в одном такте. Доминирующая архитектура при разработке JVM была 32bits, как раз для 32bit int. (Операции, которые включают 64bit long значение реализуются как частный случай).

(Примечание: последний абзац немного упрощен, учитывая возможную векторизацию и т. д., но должен дать основную идею, не погружаясь слишком глубоко в темы дизайна процессора)


EDIT: короткое добавление, сосредоточенное на примере вопроса, но в более общем смысле: можно также спросить, не было бы полезно хранить поля использование меньших типов. Для например, можно подумать, что память можно сохранить, сохранив Calendar.DAY_OF_WEEK как byte. Но здесь в игру вступает формат файла класса Java: все поля в файле класса занимают по крайней мере один "слот", которая имеет размер один int (32 бита). ("Широкие" поля,double и long, занимают два слота). Таким образом, явное объявление поля как short или byte также не сохранит память.


(почти) все операции на byte, short будет способствовать их int, например, вы не можете написать:

short x = 1;
short y = 2;

short z = x + y; //error

арифметика проще и проще при использовании int, нет необходимости, чтобы бросить.

С точки зрения пространства, это делает очень небольшая разница. byte и short усложнило бы вещи, я не думаю, что эта микро-оптимизация стоит того, так как мы говорим о фиксированной сумме переменная.

byte актуально и полезно, когда вы программируете для встроенных устройств или имеете дело с файлами/сетями. Также эти примитивы ограничены, что, если расчеты могут превысить их пределы в будущем? Попробуйте подумать о расширении для Calendar класс, который может развиваться большими числами.

также обратите внимание, что в 64-битных процессорах местные жители будут сохранены в регистрах и не будут использовать никаких ресурсов, поэтому используйте int, short и другие примитивы не не имеет никакого значения. Кроме того, многие реализации Java выравнивают переменные* (и объекты).


*byte и short занимают то же место, что и int если они местные переменные класс переменные или даже экземпляр переменные. Почему? Потому что в (большинстве) компьютерных систем адреса переменных соответствие, например, если вы используете один байт, вы фактически в итоге два байта - один для самой переменной, а другой для заполнения.

С другой стороны, в массивах, byte взять 1 байт short принимаем 2 байта и int возьмите четыре байта, потому что в массивах только начало и, возможно, конец его должен быть выровнен. Это будет иметь значение, если вы хотите использовать, например,System.arraycopy(), тогда вы действительно заметите разницу в производительности.


потому что арифметические операции проще при использовании целых чисел по сравнению с шортами. Предположим, что константы действительно были смоделированы short значения. Тогда вам придется использовать API следующим образом:

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

обратите внимание на явное приведение. Короткие значения неявно повышаются до int значения, когда они используются в арифметических операциях. (В стеке операндов шорты даже выражаются как ints.) Это было бы довольно громоздко использовать, поэтому int значения часто предпочитаемый для констант.

по сравнению с этим, выигрыш в эффективности хранения минимален, потому что существует только фиксированное число таких констант. Речь идет о 40 константах. Изменение их хранения с int to short будет безопасно для вас 40 * 16 bit = 80 byte. См.ответ для дальнейшего использования.


Если вы использовали философию, в которой интегральные константы хранятся в наименьшем типе, в который они вписываются, тогда Java будет иметь серьезную проблему: всякий раз, когда программисты пишут код с использованием интегральных констант, они должны обратить пристальное внимание на свой код, чтобы проверить, имеет ли значение тип констант, и если да, то найдите тип в документации и/или сделайте любые преобразования типов.

Итак, теперь, когда мы наметили серьезную проблему, Какие преимущества вы могли бы надеяться достичь с этой философией? Я не удивлюсь, если только runtime-наблюдаемый эффект этого изменения будет тем, какой тип вы получите, когда будете искать константу через отражение. (и, конечно же, какие бы ошибки ни вводили ленивые / невольные программисты, неправильно учитывающие типы констант)

взвешивание плюсов и минусов очень легко: это плохая философия.


сложность проектирования виртуальной машины зависит от того, сколько видов операций она может выполнять. Проще иметь четыре реализации инструкции типа "multiply" -по одной для 32-разрядного целого, 64-разрядного целого, 32-разрядной плавающей точки и 64-разрядной плавающей точки-чем иметь, в дополнение к вышеизложенному, версии для меньших числовых типов. Более интересный вопрос дизайна-почему должно быть четыре типа, а не меньше (выполнение всего целого числа вычисления с 64-разрядными целыми числами и / или выполнение всех вычислений с плавающей запятой с 64-разрядными значениями с плавающей запятой). Причина использования 32-разрядных целых чисел заключается в том, что Java, как ожидается, будет работать на многих платформах, где 32-разрядные типы могут действовать так же быстро, как 16-разрядные или 8-разрядные типы, но операции с 64-разрядными типами будут заметно медленнее. Даже на платформах, где 16-разрядные типы будут работать быстрее, дополнительные затраты на работу с 32-разрядными величинами будут компенсированы простотой предоставлено только наличие 32-разрядных типов.

Что касается выполнения вычислений с плавающей запятой на 32-разрядных значениях, преимущества немного менее ясны. Есть некоторые платформы, где вычисление, как float a=b+c+d; может быть выполнено наиболее быстро путем преобразования всех операндов в тип с более высокой точностью, добавления их, а затем преобразования результата обратно в 32-разрядное число с плавающей запятой для хранения. Есть и другие платформы, где было бы эффективнее выполнять все расчетов с использованием 32-разрядных значений с плавающей запятой. Создатели Java решили, что все платформы должны быть обязаны делать то же самое, и что они должны отдавать предпочтение аппаратным платформам, для которых 32-разрядные вычисления с плавающей запятой быстрее, чем более длинные, хотя это сильно ухудшило скорость и точность математики с плавающей запятой на типичном ПК, а также на многих машинах без единиц с плавающей запятой. Обратите внимание, кстати, что в зависимости от значений b, c и d, используя более высокоточные промежуточные вычисления при вычислении выражений, таких как вышеупомянутые float a=b+c+d; иногда будут давать результаты, которые значительно более точны, чем были бы достигнуты для всех промежуточных операндов, вычисленных при float точность, но иногда будет давать значение, которое немного менее точно. В любом случае, Sun решила, что все должно быть сделано так же, и они решили использовать минимальную точность float значения.

обратите внимание, что основной преимущества меньших типов данных становятся очевидными, когда большое их количество хранится вместе в массиве; даже если не было никакого преимущества в том, что отдельные переменные типов меньше 64-бит, стоит иметь массивы, которые могут хранить меньшие значения более компактно; наличие локальной переменной byte, а не long сохраняет семь байтов; имея массив из 1,000,000 чисел, держите каждое число как byte, а не long волны 7,000,000 байт. Поскольку каждый тип массива нужно только поддерживать несколько операций (в частности, читать один элемент, хранить один элемент, копировать диапазон элементов в массиве или копировать диапазон элементов из одного массива в другой), дополнительная сложность наличия большего количества типов массивов не так серьезна, как сложность наличия большего количества типов непосредственно используемых дискретных числовых значений.


на самом деле, это было бы небольшое преимущество. Если у вас есть

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

тогда на типичном JVM ему нужно столько же места, сколько и классу, содержащему один int. Потребление памяти округляется до следующего кратного 8 или 16 байтов (IIRC, который настраивается), поэтому случаи, когда есть реальное сохранение, довольно редки.

этот класс будет немного проще в использовании, если соответствующие Calendar методы возвратил byte. Но таких нет!--3--> методы, только get(int), который должен возвращает int из-за других полей. Каждая операция на меньших типах способствует int, поэтому вам нужно много кастинга.

скорее всего, вы либо сдадитесь и переключитесь на int или написать сеттеры, как

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

потом типа DAY_OF_WEEK не имеет значения, в любом случае.