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

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

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

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

Ответ 1

В статье Джошуа Блоха " Эффективная 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 секунды, чтобы бежать. Взятие Лонга в примитив снижает его до 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

Ответ 2

Autounboxing может привести к обнаружению NPE с трудными показателями

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

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

Ответ 3

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

Ответ 4

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

int x = 1000;
int y = 1000;

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

x == y

Это true. Не удивительно. Теперь попробуйте типы в коробке:

Integer x = 1000;
Integer y = 1000;

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

x == y

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

Ответ 5

Помимо проблем с производительностью и памятью, я хотел бы затронуть еще одну проблему: List interface будет разбит без 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!

Ответ 6

Вы действительно можете себе представить

  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. Autoboxing - это все, что позволяет вам использовать int, где ожидается Integer, с компилятором, вставляющим соответствующую конструкцию объекта. Это никоим образом не устраняет или не уменьшает потребность в int в первую очередь. С autoboxing вы получаете лучшее из обоих миров. Вы получаете Integer, созданный для вас автоматически, когда вам нужен Java-объект с кучей, и вы получаете скорость и эффективность int, когда вы просто выполняете арифметические и локальные вычисления.

Ответ 7

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

int i;
i++;

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

Ответ 8

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

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

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

Ответ 9

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

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

Ответ 10

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

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

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

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

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

Ответ 11

Несколько причин не избавиться от примитивов:

  • Обратная совместимость.

Если это исключено, любые старые программы даже не будут запускаться.

  • Переписать JVM.

Все JVM необходимо будет переписать для поддержки этой новой вещи.

  • Увеличенный объем памяти.

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

  • Проблемы с нулевым указателем.

Объявляя int i, тогда выполнение команды с помощью i не приведет к возникновению проблем, но объявление Integer i, а затем выполнение этого же приведет к NPE.

  • Вопросы равенства.

Рассмотрим этот код:

Integer i1 = 5;
Integer i2 = 5;

i1 == i2; // Currently would be false.

Будет ложным. Операторы должны быть перегружены, и это приведет к серьезному перезаписи вещей.

  • Низкая

Обертки объектов значительно медленнее, чем их примитивные копии.

Ответ 12

Объекты намного более тяжелые, чем примитивные, поэтому примитивные типы намного эффективнее, чем экземпляры классов-оболочек.

Примитивные типы очень просты: например, int 32 бит и занимает в памяти ровно 32 бита, и их можно манипулировать напрямую. Объект Integer - это полный объект, который (как любой объект) должен быть сохранен в куче и доступен только через ссылку (указатель) на него. Он, скорее всего, также занимает более 32 бит (4 байта) памяти.

Тем не менее, тот факт, что Java имеет различие между примитивными и не примитивными типами, также является признаком возраста языка программирования Java. Более новые языки программирования не имеют такого различия; компилятор такого языка достаточно умен, чтобы разобраться в себе, если вы используете простые значения или более сложные объекты.

Например, в Scala нет примитивных типов; существует класс Int для целых чисел, а Int - реальный объект (который вы можете использовать для методов и т.д.). Когда компилятор компилирует ваш код, он использует примитивные ints за кулисами, поэтому использование Int так же эффективно, как использование примитивного int в Java.

Ответ 13

В дополнение к тому, что говорили другие, примитивные локальные переменные не выделяются из кучи, а вместо этого в стеке. Но объекты выделены из кучи и, следовательно, должны быть собраны мусором.

Ответ 14

Трудно понять, какие оптимизации выполняются под обложками.

Для локального использования, когда у компилятора достаточно информации для оптимизации, исключая возможность нулевого значения, Я ожидаю, что производительность будет такой же или подобной.

Однако массивы примитивов, по-видимому, очень разные из коллекций примитивов в штучной упаковке. Это имеет смысл, учитывая, что очень мало оптимизаций возможно внутри коллекции.

Кроме того, Integer имеет гораздо более высокие логические накладные расходы по сравнению с int: теперь вам нужно беспокоиться о том, генерирует ли исключение int a = b + c; исключение.

Я использую примитивы как можно больше и полагаюсь на методы factory и autoboxing, чтобы дать мне более семантически мощные типы в коробке, когда они понадобятся.

Ответ 15

Примитивные типы имеют много преимуществ:

  • Упрощенный код для записи
  • Производительность лучше, поскольку вы не создаете экземпляр объекта для переменной
  • Поскольку они не представляют собой ссылку на объект, нет необходимости проверять нули
  • Используйте примитивные типы, если вам не нужно использовать бокс-функции.

Ответ 16

int loops = 100000000;

long start = System.currentTimeMillis();
for (Long l = new Long(0); l<loops;l++) {
    //System.out.println("Long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start));

start = System.currentTimeMillis();
for (long l = 0; l<loops;l++) {
    //System.out.println("long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start));

Миллисекунды, взятые в цикл "100000000" раз вокруг Long: 468

Миллисекунды, взятые в цикл "100000000" раз по длине: 31

На стороне примечания, я бы не прочь увидеть что-то вроде этого найти его путь в Java.

Integer loop1 = new Integer(0);
for (loop1.lessThan(1000)) {
   ...
}

Если цикл for автоматически увеличивает цикл 1 от 0 до 1000 или

Integer loop1 = new Integer(1000);
for (loop1.greaterThan(0)) {
   ...
}

Если цикл for автоматически уменьшает цикл1 1000 до 0.

Ответ 17

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

Вы должны спросить, почему требуется тип класса/объекта

Причина наличия типа объекта - облегчить нашу жизнь, когда мы имеем дело с Коллекциями. Примитивы не могут быть добавлены непосредственно в List/Map, но вам нужно написать класс оболочки. Класс Readymade Integer Classes помогает вам здесь, плюс он имеет много полезных методов, таких как Integer.pareseInt(str)

Ответ 18

Я согласен с предыдущими ответами, использование объектов-обёрток примитивов может быть дорогим. Но если производительность не критична для вашего приложения, вы избегаете переполнения при использовании объектов. Например:

long bigNumber = Integer.MAX_VALUE + 2;

Значение bigNumber - -2147483647, и вы ожидаете, что оно будет 2147483649. Это ошибка в коде, которую можно исправить, выполнив:

long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now (it is '2L').

И bigNumber будет 2147483649. Такие ошибки иногда легко пропустить и могут привести к неизвестному поведению или уязвимостям (см. CWE-190).

Если вы используете объекты-оболочки, эквивалентный код не будет компилироваться.

Long bigNumber = Integer.MAX_VALUE + 2; // Not compiling

Так что проще решить подобные проблемы, используя объекты-обертки примитивов.

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

Ответ 19

Поскольку JAVA выполняет все математические операции в примитивных типах. Рассмотрим этот пример:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li)
        if (i % 2 == 0)
            sum += i;
        return sum;
}

Здесь операции напоминания и унарные операции не могут применяться к типу Integer (Reference), компилятор выполняет распаковку и выполняет операции.

Итак, убедитесь, что многие операции с автобоксированием и распаковкой происходят в java-программе. Поскольку для выполнения этих операций требуется время.

Как правило, лучше сохранить аргументы типа Reference и результата примитивного типа.

Ответ 20

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

С другой стороны, текущая спецификация языка Java не позволяет использовать примитивные типы в параметризованных типах (обобщенных типах), в коллекциях Java или Reflection API.

Когда нашему приложению нужны коллекции с большим количеством элементов, мы должны рассмотреть возможность использования массивов с как можно более "экономичным" типом, как это показано на графике выше.

* Для получения подробной информации см. Источник: https://www.baeldung.com/java-primitives-vs-objects