Почему люди все еще используют примитивные типы в Java? - PullRequest
152 голосов
/ 05 марта 2011

Начиная с Java 5, у нас был бокс / распаковка примитивных типов, так что int оборачивается в java.lang.Integer, и так далее, и так далее.

В последнее время я вижу много новых Java-проектов (которым определенно требуется JRE по крайней мере версии 5, если не 6), которые используют int вместо java.lang.Integer, хотя это гораздо больше удобно использовать последний, так как он имеет несколько вспомогательных методов для преобразования в long значений и др.

Почему некоторые все еще используют примитивные типы в Java? Есть ли ощутимая выгода?

Ответы [ 20 ]

376 голосов
/ 05 марта 2011

В книге Джошуа Блоха Effective Java , пункт 5: «Избегайте создания ненужных объектов», он публикует следующий пример кода:

public static void main(String[] args) {
    Long sum = 0L; // uses Long, not long
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}

и запуск занимает 43 секунды. Взятие Long в примитив снижает его до 6,8 секунд ... Если есть какие-либо признаки, почему мы используем примитивы.

Отсутствие равенства собственных значений также является проблемой (.equals() довольно многословно по сравнению с ==)

для бизиклопа:

class Biziclop {

    public static void main(String[] args) {
        System.out.println(new Integer(5) == new Integer(5));
        System.out.println(new Integer(500) == new Integer(500));

        System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
    }
}

Результаты:

false
false
true
false

РЕДАКТИРОВАТЬ Почему (3) возвращает true и (4) возвращает false?

Потому что это два разных объекта. 256 целых чисел, ближайших к нулю [-128; 127] кэшируются JVM, поэтому они возвращают один и тот же объект для них. Однако за пределами этого диапазона они не кэшируются, поэтому создается новый объект. Чтобы усложнить задачу, JLS требует кэширования не менее 256 маховиков. Реализаторы JVM могут добавить больше, если они захотят, это означает, что это может выполняться в системе, где кэшируются ближайшие 1024, и все они возвращают true ... #awkward

80 голосов
/ 05 марта 2011

Autounboxing может привести к тому, что трудно обнаружить NPE

Integer in = null;
...
...
int i = in; // NPE at runtime

В большинстве ситуаций нулевое назначение in намного менее очевидно, чем выше.

39 голосов
/ 05 марта 2011

Примитивные типы:

int x = 1000;
int y = 1000;

Теперь оцените:

x == y

Это true. Вряд ли удивительно. Теперь попробуйте в штучной упаковке:

Integer x = 1000;
Integer y = 1000;

Теперь оцените:

x == y

Это false. Наверное. Зависит от времени выполнения. Достаточно ли этой причины?

39 голосов
/ 05 марта 2011

Типы в штучной упаковке имеют худшую производительность и требуют больше памяти.

35 голосов
/ 18 июля 2012

Помимо проблем с производительностью и памятью, я хотел бы предложить еще одну проблему: List интерфейс будет сломан без int.
Проблема в перегрузке remove()метод (remove(int) против remove(Object)).remove(Integer) всегда разрешает вызывать последний, поэтому вы не можете удалить элемент по индексу.

С другой стороны, при попытке добавить и удалить int:

есть ловушка.
final int i = 42;
final List<Integer> list = new ArrayList<Integer>();
list.add(i); // add(Object)
list.remove(i); // remove(int) - Ouch!
27 голосов
/ 05 марта 2011

Можете ли вы представить себе цикл

  for (int i=0; i<10000; i++) {
      do something
  }

с java.lang.Integer вместо этого?Java.lang.Integer является неизменным, поэтому каждое приращение в цикле будет создавать новый объект Java в куче, а не просто увеличивать int в стеке с помощью одной инструкции JVM.Производительность будет дьявольской.

Я бы не согласился с тем, что гораздо удобнее использовать java.lang.Integer, чем int.Напротив.Автобокс означает, что вы можете использовать int там, где в противном случае вы были бы вынуждены использовать Integer, а java-компилятор позаботится о вставке кода, чтобы создать для вас новый объект Integer.Автобокс - это все, что позволяет вам использовать int там, где ожидается Integer, с компилятором, вставляющим соответствующую конструкцию объекта.Это никоим образом не устраняет и не уменьшает потребность в int в первую очередь.С автобоксом вы получаете лучшее из обоих миров.Вы получаете Integer, созданный для вас автоматически, когда вам нужен Java-объект на основе кучи, и вы получаете скорость и эффективность int, когда вы просто выполняете арифметические и локальные вычисления.

18 голосов
/ 05 марта 2011

Примитивные типы намного быстрее:

int i;
i++;

Целое число (все числа, а также строка) является неизменяемым типом: после создания его нельзя изменить. Если бы i было Integer, тогда i++ создаст новый объект Integer - намного дороже с точки зрения памяти и процессора.

16 голосов
/ 05 марта 2011

В первую очередь привычка. Если вы программировали на Java в течение восьми лет, вы накапливаете значительное количество инерции. Зачем менять, если нет веских причин для этого? Это не значит, что использование коробочных примитивов дает дополнительные преимущества.

Другая причина состоит в том, чтобы утверждать, что null не является допустимым параметром. Было бы бессмысленно и вводить в заблуждение объявлять сумму двух чисел или переменную цикла как Integer.

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

12 голосов
/ 05 марта 2011

Кстати, у Smalltalk есть только объекты (без примитивов), и все же они оптимизировали свои маленькие целые числа (используя не все 32 бита, только 27 или около того), чтобы не выделять пространство кучи, а просто использовать специальный битовый шаблон,Также другие обычные объекты (true, false, null) имели здесь специальные битовые комбинации.

Таким образом, по крайней мере на 64-битных JVM (с пространством имен указателей на 64 бит), должно быть возможно не иметь никаких объектовInteger, Character, Byte, Short, Boolean, Float (и small Long) вообще (кроме тех, которые созданы явным new ...()), только специальные битовые комбинации, которыми могут эффективно управлять обычные операторы.

9 голосов
/ 13 апреля 2011

Не могу поверить, что никто не упомянул, что я считаю самой важной причиной: «int» - это намного проще, чем «Integer». Я думаю, что люди недооценивают важность краткого синтаксиса. Производительность на самом деле не причина их избегать, потому что большую часть времени, когда кто-то использует числа, находится в индексах цикла, а увеличение и сравнение этих значений ничего не стоит в любом нетривиальном цикле (независимо от того, используете ли вы int или Integer).

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

Другая причина заключалась в том, что (new Long (1000)) == (new Long (1000)) имеет значение false, но это просто еще один способ сказать, что «.equals» не имеет синтаксической поддержки коробочных типов (в отличие от операторов <,>, = и т. д.), поэтому мы возвращаемся к причине «более простого синтаксиса».

Я думаю, что пример непримитивного цикла Стива Йегге очень хорошо иллюстрирует мою точку зрения: http://sites.google.com/site/steveyegge2/language-trickery-and-ejb

Подумайте об этом: как часто вы используете типы функций в языках, которые имеют хороший синтаксис для них (например, любой функциональный язык, python, ruby ​​и даже C) по сравнению с Java, где вы должны имитировать их с помощью интерфейсов, таких как Runnable и вызываемые и безымянные классы.

...