Почему коллекции Java не могут напрямую хранить типы примитивов?

123

Коллекции Java хранят только объекты, а не примитивные типы; однако мы можем хранить классы-оболочки.

Почему это ограничение?

JavaUser
источник
2
Это ограничение - отстой, когда вы имеете дело с примитивами и хотите использовать очереди для отправки, и ваши скорости отправки очень высоки. Я сейчас занимаюсь проблемой автобокса, занимающего слишком много времени.
JPM
Технически примитивные типы являются объектами (точнее, одноэлементными экземплярами), они просто не определены a class, а скорее JVM. Оператор int i = 1определяет указатель на одноэлементный экземпляр объекта, который определяется intв JVM, установленный на значение, 1определенное где-то в JVM. Да, указатели в Java - это просто абстрагирование от вас с помощью языковой реализации. Примитивы нельзя использовать в качестве универсальных, потому что предикаты языка всех универсальных типов должны относиться к супертипу, Objectпоэтому они A<?>компилируются A<Object>во время выполнения.
Роберт Э. Фрай
1
Примитивные типы @RobertEFry не являются объектами в Java, поэтому все, что вы писали об экземплярах синглтонов и тому подобное, в корне неверно и сбивает с толку. Я предлагаю прочитать главу «Типы, значения и переменные» спецификации языка Java, в которой определяется, что такое объект: «Объект (§4.3.1) - это динамически создаваемый экземпляр типа класса или динамически создаваемый массив. "
typeracer

Ответы:

99

Это было дизайнерское решение Java, которое некоторые считают ошибкой. Контейнеры хотят, чтобы объекты и примитивы не были производными от Object.

Это то место, которое разработчики .NET извлекли из JVM и реализовали типы значений и обобщения, так что во многих случаях упаковка исключена. В среде CLR универсальные контейнеры могут хранить типы значений как часть базовой структуры контейнера.

Java решила добавить 100% универсальную поддержку в компилятор без поддержки JVM. JVM, какова она есть, не поддерживает «не объектный» объект. Дженерики Java позволяют вам притвориться, что оболочки нет, но вы все равно платите цену производительности бокса. Это ВАЖНО для определенных классов программ.

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

Хорошее объяснение SO о боксе: зачем некоторым языкам нужны бокс и распаковка?

И критика дженериков Java: почему некоторые утверждают, что реализация дженериков в Java плохая?

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

codenheim
источник
6
Это не ошибка, тщательно подобранный компромисс, который, я считаю, действительно очень хорошо послужил Java.
DJClayworth 04
13
Это было достаточно ошибкой, которую .NET извлек из нее и с самого начала реализовал автобоксинг и дженерики на уровне виртуальных машин без накладных расходов. Собственная попытка исправления в Java была всего лишь решением на уровне синтаксиса, которое все еще страдает от снижения производительности автобокса по сравнению с отсутствием бокса вообще. Реализация Java показала низкую производительность с большими структурами данных.
Codenheim 05
2
@mrjoltcola: IMHO, автобоксирование по умолчанию - это ошибка, но должен был быть способ пометить переменные и параметры, которые должны автоматически блокировать значения, данные им. Даже сейчас, я думаю, должны быть добавлены средства, указывающие, что определенные переменные или параметры не должны принимать новые автоматически упакованные значения [например, должна быть законной передача Object.ReferenceEqualsссылок типа, Objectкоторые идентифицируют упакованные целые числа, но это не должно быть законным для передать целочисленное значение]. Автоматическая распаковка Java - это ИМХО просто противно.
supercat 08
18

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

Вы, конечно, можете сделать это, просто ознакомившись с GNU Trove , Apache Commons Primitives или HPPC .

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

Тило
источник
11

Это сочетание двух фактов:

  • Примитивные типы Java не являются ссылочными типами (например, an intне является Object)
  • Java делает обобщения, используя стирание типов ссылочных типов (например, a List<?>действительно List<Object>во время выполнения)

Поскольку оба эти утверждения верны, общие коллекции Java не могут напрямую хранить примитивные типы. Для удобства введено автобоксирование, позволяющее автоматически упаковывать примитивные типы как ссылочные типы. Однако не заблуждайтесь, коллекции по-прежнему хранят ссылки на объекты.

Можно ли этого избежать? Может быть.

  • Если an int- это Object, то типы боксов вообще не нужны.
  • Если обобщения не выполняются с использованием стирания типа, тогда примитивы могли использоваться для параметров типа.
polygenelubricants
источник
8

Есть понятие авто-бокса и авто-распаковки. Если вы попытаетесь сохранить файл intв формате, List<Integer>компилятор Java автоматически преобразует его в файл Integer.

Джереми
источник
1
Автобокс был представлен в Java 1.5 вместе с Generics.
Джереми
1
Но это время компиляции; синтаксический сахар без повышения производительности. Компилятор Java выполняет автоматические боксы, отсюда и снижение производительности по сравнению с реализациями виртуальных машин, такими как .NET, обобщенные типы которых не включают бокс.
Codenheim 05
1
@mrjoltcola: Что ты думаешь? Я просто делился фактами, а не спорил.
Джереми
3
Я хочу сказать, что важно указать на разницу между синтаксисом и производительностью. Я также считаю свои комментарии фактом, а не аргументом. Спасибо.
Codenheim 05
2

На самом деле это не ограничение, не так ли?

Подумайте, хотите ли вы создать коллекцию, в которой хранятся примитивные значения. Как бы вы написали коллекцию, которая может хранить либо int, либо float, либо char? Скорее всего, у вас будет несколько коллекций, поэтому вам понадобятся intlist, charlist и т. Д.

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

Винсент Рамдхани
источник
7
«Как бы вы написали коллекцию, которая может хранить либо int, либо float, либо char?» - С правильно реализованными универсальными шаблонами / шаблонами, такими как другие языки, которые не платят штраф за представление всего объекта как объект.
Codenheim,
Я почти никогда за шесть лет Java не хотел хранить коллекцию примитивов. Даже в тех немногих случаях, когда я мог бы захотеть, дополнительные временные и пространственные затраты на использование эталонных объектов были незначительными. В частности, я обнаружил, что многие люди думают, что им нужен Map <int, T>, забывая, что массив прекрасно справится с этим трюком.
DJClayworth 04
2
@DJClayworth Это хорошо работает только в том случае, если ключевые значения не являются разреженными. Конечно, вы можете использовать вспомогательный массив для отслеживания ключей, но у этого есть свои проблемы: относительно эффективный доступ потребует сортировки обоих массивов в зависимости от порядка ключей, чтобы разрешить двоичный поиск, который, в свою очередь, будет производить вставку и удаление неэффективен, если вставка / удаление не структурирована таким образом, что вставленные элементы, скорее всего, окажутся там, где был ранее удаленный элемент, и / или некоторые буферы перемежаются в массивах и т. д. Ресурсы доступны, но было бы неплохо иметь в Сама Java.
JAB
@JAB На самом деле он отлично работает, если ключи редкие, просто требуется больше памяти, чем если бы они не редкие. И если их ключи редкие, это означает, что их не так много и использование Integer в качестве ключа работает нормально. Используйте тот подход, который требует меньше всего памяти. Или как хотите, если вам все равно.
DJClayworth
0

Я думаю, мы могли бы увидеть прогресс в этой области в JDK, возможно, в Java 10, основанном на этом JEP - http://openjdk.java.net/jeps/218 .

Если вы хотите избежать использования примитивов бокса в коллекциях сегодня, есть несколько сторонних альтернатив. В дополнение к ранее упомянутым сторонним опциям есть также Eclipse Collections , FastUtil и Koloboke .

Некоторое время назад было опубликовано сравнение примитивных карт под названием: Обзор большой HashMap: JDK, FastUtil, Goldman Sachs, HPPC, Koloboke, Trove . Библиотека GS Collections (Goldman Sachs) была перенесена в Eclipse Foundation и теперь называется Eclipse Collections.

Дональд Рааб
источник
0

Основная причина - стратегия дизайна Java. ++ 1) коллекции требуют объектов для манипулирования, а примитивы не являются производными от объекта, поэтому это может быть другой причиной. 2) примитивные типы данных Java не являются ссылочным типом, например. int не является объектом.

Преодолеть: -

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

user5693566
источник