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

137

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

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

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

Вилли Ментцель
источник

Ответы:

166

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

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

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

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

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

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

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

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


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

Marco13
источник
Я предполагаю, что логика того, почему операнды повышаются до int, также связана с обоснованием, используемым в C и C ++
Shafik Yaghmour
@ Marco13 «То есть явное объявление поля как короткого или байтового не спасло бы и память». это правда? Я не думаю, что это правильно.
ACV
@ACV Строго говоря, реализация могла бы выбрать для хранения более компактную форму, но формат, который предоставляется «виртуально» (то есть виртуальной машиной), будет обрабатывать значения как минимум размером int. Если у вас есть ссылка на другую реализацию, я бы обновил ответ и соответственно вставил ссылку.
Marco13
40

(Почти) Все операции 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(), тогда вы действительно заметите разницу в производительности.

Марун
источник
1
Интересный факт: если вы используете окончательные модификаторы для обоих значений, это сработает. :)
александр
7

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

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

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

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

Рафаэль Винтерхальтер
источник
5

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

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

Очень просто взвесить все за и против: это плохая философия.


источник
4

Сложность конструкции виртуальной машины зависит от того, сколько видов операций она может выполнить. Кроме того, проще иметь четыре реализации команды типа «умножение» - по одной для 32-разрядного целого, 64-разрядного целого, 32-разрядного с плавающей точкой и 64-разрядного с плавающей запятой - чем иметь к вышеупомянутому, версии для меньших числовых типов также. Более интересным вопросом проектирования является то, почему должно быть четыре типа, а не меньше (выполнение всех целочисленных вычислений с 64-разрядными целыми числами и / или выполнение всех вычислений с плавающей запятой с 64-разрядными значениями с плавающей запятой). Причина использования 32-разрядных целых чисел заключается в том, что Java должна была работать на многих платформах, где 32-разрядные типы могут обрабатываться так же быстро, как 16-разрядные или 8-разрядные типы, но операции с 64-разрядными типами будут заметно помедленнее.только 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сохранять семь байтов; имея массив 1000000 чисел держать каждое число в виде , byteа неlongволны 7 000 000 байт. Поскольку каждый тип массива должен поддерживать только несколько операций (в частности, чтение одного элемента, сохранение одного элемента, копирование диапазона элементов в массиве или копирование диапазона элементов из одного массива в другой), дополнительная сложность состоит в том, что Типы массивов не так серьезны, как сложность наличия большего количества типов непосредственно используемых дискретных числовых значений.

Supercat
источник
2

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

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

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

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

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

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

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

maaartinus
источник