tl;dr
is there memory overhead?
- Yes, objects take more memory than primitives.
- Auto-boxing has a tiny bit of cost in both memory and performance.
What happens inside Number class when I assign these 3 data types to it?
Nothing special happens when you assign a Integer
, Long
, or Double
object to your List<Number>
.
But something special is happening when you assign a primitive to an object of its wrapping class: auto-boxing of primitives into objects. This occurs in your first three lines where you populate your Double
, Integer
, and Long
object by assigning a primitive value.
If your environment is so severely constrained (limited in memory), you should not be using any of those initial-cap class names: Byte
, Short
, Integer
, Long
, Float
, Double
, Number
. Use primitive arrays with primitive types. But do use the types (discussed below), rather than invent your own bit-by-bit byte-by-byte management unless you have a very clear proven reason.
can I put Double there and expect no precision loss when I will try to get it back as Double?
There is no conversion when a Double
object is held as a Number
. A Double
object is a Number
already, by inheritance. Every Double
object is a Number
, but not every Number
is a Double
.
Details
Number
is an abstract class (see Tutorial), meaning that it is not meant to be directly instantiated. It is designed to be subclassed, and those subclasses in turn can be instantiated.
You need to learn the difference between primitive types and object types. Java offers both kinds of type systems. Primitive values (see Tutorial) are not object-oriented. Primitives were designed into Java to facilitate (a) programmers not skilled in OOP to learn the new Java language, and (b) porting code from other languages that have a similar type system (such as C). Also, primitives have the advantages of taking little memory and being fast to work with. Objects, in contrast, take more memory and are not as fast in execution to work with, but are much more flexible and sophisticated.
Some people have argued that you can design a programming language to have the best of both, exposing only object types while backing some of them with primitive types… but that is not what Java (currently) does, so here we will set that topic aside.
Java mixes the primitive and object types within the same code. There are even equivalent object types for each of the numeric primitives. Notice the letter case conventions, where lowercase indicates primitive type while initial uppercase indicates object type. Each of these initial-cap classes listed below are subclass of Number
.
- For the primitive
byte
, we have the class Byte
, both 8-bit (octet) signed integer number holders, with a range of -128 to 127 (inclusive).
- For the primitive
short
, we have the class Short
, both 16-bit signed integer number holders, with a range of -32,768 to 32,767 (inclusive).
- For the primitive
int
, we have the class Integer
, both 32-bit signed integer number holders, with a range of -231 to a maximum value of 231-1 (roughly ± 2 billion).
- For the primitive
long
, we have the class Long
, both 64-bit signed integer number holders, with a range of -263 and a maximum value of 263-1.
- For the primitive
float
, we have the class Float
, both 32-bit signed floating-point numbers.
- For the primitive
double
, we have the class Double
, both 64-bit signed floating-point numbers.
Why do we bother with the wrapper classes if we already have the primitives? For compatibility with other code that expects objects. The biggest example is the Java Collections Framework.
Recent generations of Java added auto-boxing to bridge the gap between the type systems by generating conversion code at compile time. Auto-boxing converts primitive data types to their matching wrapper class.
Auto-boxing was added to make life easier for human-programmers, but is more work for the computer at runtime. Boxing means looking up the matching class, instantiating an object of that class, and assigning the primitive’s value to that object. Unboxing means the reverse, the value must be extracted from the object and placed in memory where the primitive lives.
In many apps, this overhead added by boxing-and-unboxing is negligible to the overall performance of the app. But in more extreme cases where large counts of numbers are being processed often, a programmer may decide to avoid the boxing, avoid the objects, and use only primitives.
Double someDouble = 0.24;
Integer someInteger = 234;
Long someLong = 253263632L;
In each of the three lines above, you have a primitive value on the right being auto-boxed into an object on the left. The auto-boxing feature of Java makes this look nearly invisible, because on conventional computer environments we generally do not care about the hit on performance and memory involved with auto-boxing. But if programming for constrained environments, you may want to avoid the Number
objects and the List
objects. But then you give up the convenience of the polymorphism (treating Double
& Integer
& Long
all as Number
).
Likewise, I assume your doSomeMathOnNumber(n)
method is doing some unboxing, going from objects back to primitives. More memory and CPU cycles used.
(for best performance) I'm using byte array and ByteBuffer and I put my measurements with diffrent types in it,
While I am not an expert in programming in constrained environments, I would guess you are working too hard. I suspect simple Java arrays holding the byte
, short
, etc. types would meet your needs.
For more info, search Stack Overflow for Question such as this, Why do we use autoboxing and unboxing in Java?.
By the way, the floating-point types trade away accuracy for speed of execution. They are not appropriate for matters where accuracy matters, such as tracking money. For such matters, use the BigDecimal
class, slower but accurate.