Module 4: Primitive Wrapper Classes

Module Overview

Learn about primitive wrapper classes, autoboxing, and unboxing in Java.

Learning Objectives

  • Understand what Java collections are and when to use them
  • Learn the core interfaces of the Collections Framework
  • Explore implementation classes for Lists, Sets, and Maps
  • Utilize collection utility methods for common operations

Key Concepts

Java Collections Framework Overview

The Java Collections Framework (JCF) is a unified architecture for representing and manipulating collections of objects. It provides standard data structures like lists, sets, and maps, along with algorithms for searching, sorting, and manipulating these collections.

Core Collection Interfaces:

  • Collection - The root interface with basic methods like add(), remove(), and contains()
  • List - An ordered collection that allows duplicate elements
  • Set - A collection that cannot contain duplicate elements
  • Queue - A collection designed for holding elements prior to processing
  • Map - An object that maps keys to values, with no duplicate keys allowed

Primitive Wrapper Classes

What are they?

There is a set of classes that we refer to as "primitive wrapper" classes that correspond to the primitive types in Java. For example, the double primitive type has a corresponding Double class.

They're classes that contain a member variable of the relevant primitive type and some helpful additional methods.

Note that primitive types are not objects and are thus stored by value. The wrapper types are objects and are thus stored by reference. So a variable of type, Integer , actually contains a reference to an Integer object. But a variable of type, int , simply contains the integer value of that int. Because the wrapper classes are object types, remember to use equals() when comparing instances of these wrapper classes: do NOT use == (which will make a reference comparison instead of a value comparison!)

Primitive Types and Their Wrapper Classes:

Primitive Type Wrapper class
booleanBoolean
byteByte
charCharacter
shortShort
intInteger
longLong
floatFloat
doubleDouble

Notice that most of the wrapper class names match the primitive type names, but just with a capital letter, denoting it as a class. The two exceptions are char / Character and int / Integer. These primitive names are historical, coming from the C programming language, and you'll just need to remember that their primitives have the shortened name and the wrapper class uses the full word name for those two cases. But it probably won't be too hard as you're already familiar with int s and probably won't use char / Character all that often.

Why do they exist?

These wrapper classes offer a few things:

  • You can set a wrapper object reference to null, but you cannot set a primitive to null
  • Some classes accept only objects, so they cannot accept an int/float/double... (e.g. ArrayList), so to use them, you'll need to use the wrapper classes
  • The wrapper classes offer some helpful methods: converting between types, to/from String etc.

Immutability

We may have discussed the concept of immutability--a class whose instances cannot be modified in any way once they are constructed. If you want to "modify" the value/state of an immutable class instance, you'll need to call a method or operation that creates a new instance. This applies to each of these primitive wrapper classes: All primitive wrapper classes are immutable.

Some primitive wrapper methods for conversion

Explicitly converting between primitives, wrappers, String (using int/Integer as an example):

Between primitive/wrapper types:
  • int -> Integer: Integer's class method, valueOf(int), converts an int to an Integer:
    Integer wrapperInteger = Integer.valueOf(22);
    This is done by Java behind the scenes in:
    Integer wrapperInteger = 22;
  • Integer -> int: Integer's instance method, intValue(), converts an Integer to an int:
    Integer wrapperInteger = new Integer(22);
    int primitiveInt = wrapperInteger.intValue();
    This is done by Java behind the scenes in:
    int primitiveInt = wrapperInteger;
From String to different types:
  • String -> int: Use Integer's class method, parseInt(String):
    int primitiveInt = Integer.parseInt("22");
  • String -> Integer: Use valueOf(String):
    Integer wrapperInteger = Integer.valueOf("22");
From different types to String:
  • int -> String: Use Integer.toString(int):
    String intString = Integer.toString(22);
  • Integer -> String: Use toString() instance method:
    Integer wrapperInteger = Integer.valueOf(22);
    String integerString = wrapperInteger.toString();
Summary of Conversions:
From Type To Type method Example
int Integer Integer.valueOf(int) Integer wrapperInteger = Integer.valueOf(22);
Integer int Integer#intValue() Integer wrapperInteger = new Integer(22); int primitiveInt = wrapperInteger.intValue();
String int Integer.parseInt(String) int primitiveInt = Integer.parseInt("22");
String Integer Integer.valueOf(String) Integer wrapperInteger = Integer.valueOf("22");
int String Integer.toString(int) String intString = Integer.toString(22);
Integer String Integer#toString() Integer wrapperInteger = Integer.valueOf(22); String integerString = wrapperInteger.toString();

There are analogous conversion methods for the other wrapper types. For example, Double.valueOf(double), doublePrimitive.doubleValue()... search google for "Java Double class" or "Java Boolean class" and you should quickly find the javadoc documentation from Oracle on the relevant class.

Lists

Lists are ordered collections that allow duplicate elements. Elements can be accessed by their integer index.

Common List Implementations:

  • ArrayList - Resizable array implementation; fast for random access, slower for insertions/deletions
  • LinkedList - Doubly-linked list implementation; fast for insertions/deletions, slower for random access
  • Vector - Legacy synchronized list implementation (thread-safe)
  • Stack - Legacy LIFO (Last-In-First-Out) stack implementation

Example:


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        // ArrayList example
        List arrayList = new ArrayList<>();
        arrayList.add("Java");
        arrayList.add("Python");
        arrayList.add("JavaScript");
        arrayList.add("Java");  // Duplicates allowed
        
        System.out.println("ArrayList: " + arrayList);
        System.out.println("Element at index 1: " + arrayList.get(1));
        
        // LinkedList example
        List linkedList = new LinkedList<>(arrayList);  // Initialize with another collection
        linkedList.add(0, "C++");  // Add at specific position
        linkedList.remove("Java");  // Remove first occurrence
        
        System.out.println("LinkedList: " + linkedList);
        
        // Common operations
        System.out.println("Size: " + linkedList.size());
        System.out.println("Contains 'Python'? " + linkedList.contains("Python"));
        System.out.println("Index of 'JavaScript': " + linkedList.indexOf("JavaScript"));
    }
}
                

Sets

Sets are collections that cannot contain duplicate elements. They model the mathematical set abstraction.

Common Set Implementations:

  • HashSet - Uses hash table for storage; doesn't guarantee order, offers constant-time performance
  • LinkedHashSet - Hash table with linked list, maintains insertion order
  • TreeSet - Based on TreeMap (Red-Black tree), elements stored in sorted order

Example:


import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

public class SetExample {
    public static void main(String[] args) {
        // HashSet example
        Set hashSet = new HashSet<>();
        hashSet.add(10);
        hashSet.add(5);
        hashSet.add(20);
        hashSet.add(10);  // Duplicate - will be ignored
        
        System.out.println("HashSet: " + hashSet);  // Order not guaranteed
        
        // LinkedHashSet example - maintains insertion order
        Set linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add(10);
        linkedHashSet.add(5);
        linkedHashSet.add(20);
        
        System.out.println("LinkedHashSet: " + linkedHashSet);  // Maintains insertion order
        
        // TreeSet example - maintains sorted order
        Set treeSet = new TreeSet<>();
        treeSet.add(10);
        treeSet.add(5);
        treeSet.add(20);
        
        System.out.println("TreeSet: " + treeSet);  // Natural ordering (ascending)
        
        // Set operations
        Set set1 = new HashSet<>(Set.of(1, 2, 3, 4, 5));
        Set set2 = new HashSet<>(Set.of(4, 5, 6, 7, 8));
        
        // Union
        Set union = new HashSet<>(set1);
        union.addAll(set2);
        System.out.println("Union: " + union);
        
        // Intersection
        Set intersection = new HashSet<>(set1);
        intersection.retainAll(set2);
        System.out.println("Intersection: " + intersection);
        
        // Difference
        Set difference = new HashSet<>(set1);
        difference.removeAll(set2);
        System.out.println("Difference (set1 - set2): " + difference);
    }
}
                

Maps

Maps are objects that map keys to values. A map cannot contain duplicate keys, and each key can map to at most one value.

Common Map Implementations:

  • HashMap - General-purpose implementation based on a hash table
  • LinkedHashMap - Hash table with linked list, maintains insertion order of keys
  • TreeMap - Based on a Red-Black tree, keys maintained in sorted order
  • Hashtable - Legacy synchronized implementation (thread-safe)

Example:


import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class MapExample {
    public static void main(String[] args) {
        // HashMap example
        Map hashMap = new HashMap<>();
        hashMap.put("John", 25);
        hashMap.put("Alice", 30);
        hashMap.put("Bob", 28);
        hashMap.put("John", 26);  // Overwrites previous value for "John"
        
        System.out.println("HashMap: " + hashMap);  // Order not guaranteed
        
        // LinkedHashMap example - maintains insertion order
        Map linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("John", 25);
        linkedHashMap.put("Alice", 30);
        linkedHashMap.put("Bob", 28);
        
        System.out.println("LinkedHashMap: " + linkedHashMap);  // Maintains insertion order
        
        // TreeMap example - maintains sorted key order
        Map treeMap = new TreeMap<>();
        treeMap.put("John", 25);
        treeMap.put("Alice", 30);
        treeMap.put("Bob", 28);
        
        System.out.println("TreeMap: " + treeMap);  // Sorted by keys
        
        // Common operations
        System.out.println("Value for 'Alice': " + hashMap.get("Alice"));
        System.out.println("Contains key 'David'? " + hashMap.containsKey("David"));
        System.out.println("Contains value 28? " + hashMap.containsValue(28));
        
        // Iterating over a Map
        System.out.println("Iterating over HashMap:");
        for (Map.Entry entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // Using forEach (Java 8+)
        System.out.println("Using forEach:");
        hashMap.forEach((key, value) -> System.out.println(key + ": " + value));
    }
}
                

Additional Resources