Почему Java API использует int вместо короткого или байтового?

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

Пример: Поле DAY_OF_WEEK в classе Calendar использует int .

Если разница слишком минимальна, то почему эти типы данных ( short , int ) существуют вообще?

Некоторые из причин уже указаны. Например, тот факт, что «… (почти) все операции над байтом, короткие будут продвигать эти примитивы в int» . Однако очевидным следующим вопросом будет: ПОЧЕМУ эти типы продвигаются к int ?

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

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

Теперь, конечно, можно сказать, что «… очевидным следующий вопрос будет: ПОЧЕМУ эти инструкции предлагаются только для intlong )?» ,

Одна из причин упоминается в упомянутой выше JVM Spec:

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

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

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


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

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

 short x = 1; short y = 2; short z = x + y; //error 

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

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

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

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


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

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

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

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

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

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

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

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

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

Сложность проектирования виртуальной машины зависит от количества видов операций, которые она может выполнять. Легче иметь четыре реализации команды типа «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 ему нужно столько места, сколько class, содержащий один int . Потребление памяти округляется до следующего кратного 8 или 16 байтов (это настраивается IIRC), поэтому случаи, когда есть реальная экономия, довольно редки.

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

Скорее всего, вы либо откажетесь, либо переключитесь на int или write seters like

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

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

  • Итерация по интерфейсу
  • Есть ли 128-битное целое число в C ++?
  • Какой примитивный тип данных - time_t?
  • Каковы различия между типами () и isinstance ()?
  • Как найти числовые столбцы в Pandas?
  • PostgreSQL: разница между текстом и varchar (характер меняется)
  • Типы Haskell расстраивают простую «среднюю» функцию
  • Получение типа System.Type из частичного имени типа
  • что такое неподписанный тип данных?
  • Создайте словарь Swift, где ключ «Тип»?
  • java: конвертировать float в String и String для float
  • Давайте будем гением компьютера.