Как работает класс Number (в Java)? - PullRequest
0 голосов
/ 05 июля 2018

Справочная информация:

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

Например - для хранения температуры я использую 8-битный тип данных, для хранения некоторых других вещей я использую 16-битный шорт, и в некоторых случаях у меня двойной тип данных.

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

Что я пытаюсь сделать

Я хочу создать список всех измерений с разными типами данных:

Double someDouble = 0.24;
Integer someInteger = 234;
Long someLong = 253263632L;

List<Number> measurements = new ArrayList<>();
measurements.add(someDouble);
measurements.add(someInteger);
measurements.add(someLong);

// later I have to multiply all measurements by their multipliers
// and do some other math stuff
// My goal is to do all math in one loop without care about data type
foreach(Number n: measurements) {
    doSomeMathOnNumber(n);
}

Мой вопрос:

Что происходит внутри класса Number, когда я назначаю ему эти 3 типа данных?

Точнее:

  • есть ли накладные расходы памяти?
  • можно ли поставить Double там и не ожидать потери точности, когда я попытаюсь вернуть его как Double?

1 Ответ

0 голосов
/ 06 июля 2018

ТЛ; др

есть ли накладные расходы памяти?

  • Да, объекты занимают больше памяти, чем примитивы.
  • Автобокс имеет небольшую стоимость как памяти, так и производительности.

Что происходит внутри класса Number, когда я назначаю ему эти 3 типа данных?

Ничего особенного не происходит, когда вы назначаете объект Integer, Long или Double для List<Number>.

Но что-то особенное происходит , когда вы назначаете примитив объекту его класса-обертки: автоматическая упаковка примитивов в объекты. Это происходит в первых трех строках, где вы заполняете объект Double, Integer и Long, присваивая примитивное значение.

Если ваша среда настолько сильно ограничена (ограничена в памяти), вы должны не использовать любое из названий классов начальной заглавной буквы: Byte, Short, Integer, Long, Float, Double, Number. Используйте примитивные массивы с примитивными типами. Но делайте используйте типы (обсуждаемые ниже) вместо того, чтобы придумывать свое побитовое побитовое управление, если у вас нет явной доказанной причины.

можно ли поставить Double там и не ожидать потери точности, когда я попытаюсь вернуть его как Double?

Нет преобразования, когда объект Double удерживается как Number. Double объект уже Number по наследованию . Каждый Double объект Number, но не каждый Number Double.

Подробнее

Number - это абстрактный класс (см. Tutorial ), означающий, что он не предназначен для непосредственного создания экземпляра. Он рассчитан на подкласса , и эти подклассы, в свою очередь, могут быть созданы.

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

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

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

  • Для примитива byte у нас есть класс Byte, оба 8-бит (октет) держатели целых чисел со знаком, в диапазоне от -128 до 127 (включительно).
  • Для примитива short у нас есть класс Short, оба 16-битные держатели целых чисел со знаком, в диапазоне от -32 768 до 32 767 (включительно) .
  • Для примитива int у нас есть класс Integer, оба 32-битные держатели целых чисел со знаком, с диапазоном -2 31 до максимального значения 2 31 -1 (примерно ± 2 млрд.).
  • Для примитива long у нас есть класс Long, оба держателя 64-битных целых чисел со знаком, с диапазоном -2 63 и максимальным значением 2 63 -1.
  • Для примитива float у нас есть класс Float, оба 32-битных знака с плавающей запятой числа.
  • Для примитива double у нас есть класс Double, оба 64-битных знака с плавающей запятой числа.

Почему мы беспокоимся о классах-обёртках, если у нас уже есть примитивы? Для совместимости с другим кодом, который ожидает объекты. Самый большой пример - Java Collections Framework .

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

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

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

Double someDouble = 0.24;
Integer someInteger = 234;
Long someLong = 253263632L;

В каждой из трех строк выше у вас есть примитивное значение справа, которое автоматически помещается в объект слева. Функция автобоксирования в Java делает этот вид почти незаметным, потому что в обычных компьютерных средах мы, как правило, не заботимся о снижении производительности и памяти, связанных с автобоксом. Но если вы программируете для ограниченных сред, вы можете избегать объектов Number и List. Но затем вы отказываетесь от удобства полиморфизма (рассматривая Double & Integer & Long все как Number).

Аналогично, я предполагаю, что ваш метод doSomeMathOnNumber(n) выполняет некоторую распаковку, переходя от объектов обратно к примитивам. Больше памяти и циклов ЦП.

(для лучшей производительности) Я использую байтовый массив и ByteBuffer и помещаю свои измерения с разными типами,

Хотя я не эксперт в программировании в стесненных условиях, я думаю, вы слишком усердно работаете. Я подозреваю, что простые массивы Java, содержащие типы byte, short и т. Д., Будут соответствовать вашим потребностям.

Для получения дополнительной информации, поиск переполнения стека для вопроса, такого как этот, Почему мы используем автобокс и распаковку в Java? .


Между прочим, типы с плавающей запятой уступают точности для скорости выполнения. Они не подходят для вопросов, где важна точность, таких как отслеживание денег. Для таких целей используйте класс BigDecimal, медленный, но точный.

...