DEV Community

gaurbprajapati
gaurbprajapati

Posted on

Java: Primitive `int` vs Wrapper `Integer` - Complete Guide

Table of Contents

  1. Overview
  2. Key Differences
  3. Memory Management
  4. Integer Caching Behavior
  5. Comparison Examples
  6. Compiler Execution Flow
  7. Best Practices
  8. Common Pitfalls
  9. Performance Considerations
  10. 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    │     │
│  └─────────────┘    └─────────────┘    └─────────────┘     │
└─────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Integer Object Storage

┌─────────────────────────────────────────────────────────────┐
│                        STACK MEMORY                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐     │
│  │   main()    │    │     a1      │    │     b1      │     │
│  │   frame     │    │   ┌─────┐   │    │   ┌─────┐   │     │
│  │             │    │   │0x100│   │    │   │0x100│   │     │
│  │             │    │   └─────┘   │    │   └─────┘   │     │
│  │             │    │  8 bytes    │    │  8 bytes    │     │
│  └─────────────┘    └─────────────┘    └─────────────┘     │
└─────────────────────────────────────────────────────────────┘
                              │                    │
                              ▼                    ▼
┌─────────────────────────────────────────────────────────────┐
│                        HEAP MEMORY                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐                                            │
│  │ Address     │                                            │
│  │ 0x100       │                                            │
│  │ ┌─────┐     │                                            │
│  │ │ 10  │     │ ← Shared cached object                     │
│  │ └─────┘     │                                            │
│  └─────────────┘                                            │
└─────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Comparison Examples

Example 1: Primitive Comparison

int a = 10;
int b = 10;
System.out.println(a == b);  // true (value comparison)
Enter fullscreen mode Exit fullscreen mode

Example 2: Cached Integer Comparison

Integer a1 = 10;
Integer b1 = 10;
System.out.println(a1 == b1);  // true (same cached object)
Enter fullscreen mode Exit fullscreen mode

Example 3: Non-Cached Integer Comparison

Integer a11 = 250;
Integer b11 = 250;
System.out.println(a11 == b11);  // false (different objects)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 */ }
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls

1. Reference Comparison with ==

Integer a = 1000;
Integer b = 1000;
System.out.println(a == b);  // false (different objects)
Enter fullscreen mode Exit fullscreen mode

2. NullPointerException with Unboxing

Integer value = null;
int result = value;  // NullPointerException!
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Garbage Collection Impact

// int: No GC impact
int value = 42;  // Stack allocation

// Integer: GC overhead
Integer value = 42;  // Heap allocation, subject to GC
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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)