Table of Contents
- Overview
- Key Differences
- Memory Management
- Integer Caching Behavior
- Comparison Examples
- Compiler Execution Flow
- Best Practices
- Common Pitfalls
- Performance Considerations
- Use Cases
Overview
Java provides two ways to work with integer values:
-
int
: Primitive data type -
Integer
: Wrapper class (object)
Understanding the differences is crucial for writing correct, efficient, and maintainable code.
Key Differences
Aspect | int |
Integer |
---|---|---|
Type | Primitive | Object (Wrapper class) |
Memory Location | Stack | Stack (reference) + Heap (object) |
Memory Size | 4 bytes | 8 bytes (reference) + ~16 bytes (object) |
Default Value | 0 |
null |
Null Support | No | Yes |
Collections | Cannot be used directly | Can be used directly |
Methods | None | Rich API available |
Comparison | Value-based | Reference-based (with == ) |
Memory Management
Primitive int
Storage
┌─────────────────────────────────────────────────────────────┐
│ STACK MEMORY │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ main() │ │ a │ │ b │ │
│ │ frame │ │ ┌─────┐ │ │ ┌─────┐ │ │
│ │ │ │ │ 10 │ │ │ │ 10 │ │ │
│ │ │ │ └─────┘ │ │ └─────┘ │ │
│ │ │ │ 4 bytes │ │ 4 bytes │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
Integer
Object Storage
┌─────────────────────────────────────────────────────────────┐
│ STACK MEMORY │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ main() │ │ a1 │ │ b1 │ │
│ │ frame │ │ ┌─────┐ │ │ ┌─────┐ │ │
│ │ │ │ │0x100│ │ │ │0x100│ │ │
│ │ │ │ └─────┘ │ │ └─────┘ │ │
│ │ │ │ 8 bytes │ │ 8 bytes │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ HEAP MEMORY │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Address │ │
│ │ 0x100 │ │
│ │ ┌─────┐ │ │
│ │ │ 10 │ │ ← Shared cached object │
│ │ └─────┘ │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
Integer Caching Behavior
Java caches Integer
objects for values from -128 to 127. This optimization reduces memory usage and improves performance.
Cache Range
// These all reference the same cached object
Integer a = 10; // cached
Integer b = Integer.valueOf(10); // cached
Integer c = new Integer(10); // new object (deprecated)
System.out.println(a == b); // true (same cached object)
System.out.println(a == c); // false (different objects)
Cache Configuration
// Cache size can be configured (default: -128 to 127)
// Set via system property: java.lang.Integer.IntegerCache.high
// Example: -Djava.lang.Integer.IntegerCache.high=1000
Comparison Examples
Example 1: Primitive Comparison
int a = 10;
int b = 10;
System.out.println(a == b); // true (value comparison)
Example 2: Cached Integer Comparison
Integer a1 = 10;
Integer b1 = 10;
System.out.println(a1 == b1); // true (same cached object)
Example 3: Non-Cached Integer Comparison
Integer a11 = 250;
Integer b11 = 250;
System.out.println(a11 == b11); // false (different objects)
Example 4: Proper Integer Comparison
Integer a = 250;
Integer b = 250;
System.out.println(a.equals(b)); // true (value comparison)
System.out.println(a.intValue() == b.intValue()); // true (value comparison)
Compiler Execution Flow
For Integer a1 = 10;
Step 1: Lexical Analysis
Tokens: [Integer] [a1] [=] [10] [;]
Step 2: Syntax Analysis
Parse tree validates variable declaration structure
Step 3: Semantic Analysis
- Validate 'Integer' type ✓
- Validate 'a1' identifier ✓
- Check type compatibility: int(10) → Integer ✓
- Identify autoboxing operation
Step 4: Autoboxing Resolution
Integer a1 = 10; → Integer a1 = Integer.valueOf(10);
Step 5: Integer Cache Lookup
Integer.valueOf(10):
- Check if 10 is in cache range (-128 to 127) ✓
- Return reference to existing cached object
- No new object created
Step 6: Bytecode Generation
bipush 10 // Push int value 10 onto stack
invokestatic #2 // Call Integer.valueOf(10)
astore_1 // Store result in local variable a1
Runtime Execution
1. Push 10 onto operand stack
2. Call Integer.valueOf(10)
3. Integer.valueOf() checks cache
4. Returns cached Integer object (address 0x100)
5. Store reference in local variable a1
Best Practices
1. Use Appropriate Comparison Methods
// ❌ Wrong - compares references
Integer a = 1000;
Integer b = 1000;
if (a == b) { /* unreliable */ }
// ✅ Correct - compares values
if (a.equals(b)) { /* reliable */ }
if (a.intValue() == b.intValue()) { /* reliable */ }
if (Objects.equals(a, b)) { /* null-safe */ }
2. Choose the Right Type
// Use int for:
- Simple arithmetic operations
- Loop counters
- Array indices
- When null is not needed
- Performance-critical code
// Use Integer for:
- Collections (List<Integer>, Map<Integer, String>)
- When null values are possible
- Working with generics
- API methods that require objects
3. Handle Null Values
Integer value = getValueFromAPI(); // might return null
// ❌ Wrong - NullPointerException risk
int result = value + 10;
// ✅ Correct - null-safe
int result = (value != null) ? value + 10 : 10;
int result = Optional.ofNullable(value).orElse(0) + 10;
4. Use Autoboxing Carefully
// Autoboxing is convenient but can hide performance issues
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// ❌ Inefficient - multiple autoboxing operations
for (int i = 0; i < numbers.size(); i++) {
int sum = numbers.get(i) + i; // autoboxing + unboxing
}
// ✅ More efficient
for (Integer num : numbers) {
int sum = num + num; // no autoboxing
}
Common Pitfalls
1. Reference Comparison with ==
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // false (different objects)
2. NullPointerException with Unboxing
Integer value = null;
int result = value; // NullPointerException!
3. Performance Issues with Excessive Boxing
// ❌ Poor performance
for (int i = 0; i < 1000000; i++) {
Integer boxed = i; // Creates 1 million objects
}
// ✅ Better performance
for (int i = 0; i < 1000000; i++) {
int primitive = i; // No object creation
}
4. Inconsistent Behavior with Cache
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true (cached)
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false (not cached)
Performance Considerations
Memory Usage
// int: 4 bytes per variable
int[] array = new int[1000]; // 4KB
// Integer: 8 bytes reference + ~16 bytes object per element
Integer[] array = new Integer[1000]; // 24KB (when populated)
Access Speed
// int: Direct access (faster)
int value = 42;
int result = value * 2;
// Integer: Indirect access via reference (slower)
Integer value = 42;
int result = value * 2; // Unboxing required
Garbage Collection Impact
// int: No GC impact
int value = 42; // Stack allocation
// Integer: GC overhead
Integer value = 42; // Heap allocation, subject to GC
Use Cases
When to Use int
// 1. Simple arithmetic
int sum = a + b;
int product = x * y;
// 2. Loop counters
for (int i = 0; i < 100; i++) {
// loop body
}
// 3. Array indices
int[] array = new int[10];
for (int i = 0; i < array.length; i++) {
array[i] = i;
}
// 4. Performance-critical code
public int calculateSum(int[] numbers) {
int sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
}
When to Use Integer
// 1. Collections
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Map<Integer, String> mapping = new HashMap<>();
Java generics work through type erasure - at runtime, all generic types become Object. Since primitives can't be Object, they can't be used in generics.
// 2. Nullable values
public Integer findUserAge(String userId) {
User user = userRepository.findById(userId);
return user != null ? user.getAge() : null;
}
// 3. Generic types
public class Box<T> {
private T value;
// T can be Integer but not int
}
// 4. API methods requiring objects
public void processValue(Integer value) {
if (value != null) {
// process the value
}
}
Summary
Understanding the differences between int
and Integer
is essential for:
- Writing correct comparison logic
- Optimizing performance
- Avoiding common pitfalls
- Making appropriate design decisions
Key Takeaway: Always use .equals()
for Integer
comparisons and choose the appropriate type based on your specific use case.
This documentation should be shared with all team members working on Java projects to ensure consistent understanding and best practices.
Top comments (0)