Wrapper types and boxing

Java · Essentials · 8 min

Collections and generics only hold objects, so you meet wrapper types the moment you store numbers in a List or Map.

Each primitive has a wrapper class in java.lang: Integer, Long, Double, Boolean, Character and so on. Wrappers are objects, so they can be stored in collections (List<Integer>), be null, and offer useful static methods.

The compiler converts automatically between the two forms. Autoboxing turns int into Integer when needed, and unboxing does the reverse:

List<Integer> ids = new ArrayList<>();
ids.add(42);          // boxing
int first = ids.get(0); // unboxing

The convenient conversions hide two traps:

  • Unboxing null throws NullPointerException. If a Map<String, Integer> has no entry, int n = map.get("x"); crashes.
  • == on wrappers compares references. Small values (−128 to 127) are cached, so Integer a = 100, b = 100; a == b is true, but with 1000 it is usually false. Use equals, or unbox first.

Parsing text into numbers is a daily task: Integer.parseInt("42"), Double.parseDouble("3.5"), Long.parseLong(...). Invalid input throws NumberFormatException, so validate or catch it when parsing user input. The reverse is String.valueOf(n) or Integer.toString(n).

Other handy members: Integer.MAX_VALUE, Integer.compare(a, b), Character.isDigit(c), Character.isLetter(c).

In performance-critical loops, prefer primitives: boxing allocates objects, and a List<Integer> of a million numbers uses several times more memory than an int[].

Example

import java.util.HashMap;
import java.util.Map;

public class Boxing {
    public static void main(String[] args) {
        int port = Integer.parseInt("8080");
        double ratio = Double.parseDouble("0.75");
        System.out.println(port + ratio);          // 8080.75

        Integer a = 1000, b = 1000;
        System.out.println(a == b);                // false (different objects)
        System.out.println(a.equals(b));           // true

        Map<String, Integer> stock = new HashMap<>(Map.of("apples", 5));
        Integer pears = stock.get("pears");        // null, fine as Integer
        int safe = stock.getOrDefault("pears", 0); // 0
        System.out.println(pears + " " + safe);    // null 0
        try {
            Integer.parseInt("12abc");
        } catch (NumberFormatException e) {
            System.out.println("not a number");
        }
    }
}

Remember

  • Wrappers let primitives live in collections and be null
  • Compare wrapper values with equals, not ==
  • Integer.parseInt and friends convert text to numbers and throw on bad input

Watch out for

  • Unboxing a null Integer (e.g. from map.get) into an int throws NullPointerException.

Check yourself

  1. Given `Map<String,Integer> m = new HashMap<>();` what happens on `int n = m.get("k");`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.oracle.com/javase/tutorial/java/data/autoboxing.html