REW

Which Two Cannot Be Stored In An ArrayList?

Published Aug 29, 2025 4 min read
On this page

Two types of items that cannot be stored directly in an ArrayList are primitive data types and heterogeneous collections (without a common base type). The ArrayList class, part of Java's Collections Framework, is fundamentally designed to hold objects, which is a key distinction from standard arrays.

1. Primitive data types

Primitive data types, such as int, double, boolean, and char, cannot be stored directly in an ArrayList. This is due to the nature of the Collections Framework, which was built to work with objects and not the value-based primitive types.

The solution: Wrapper classes and autoboxing

To overcome this limitation, Java provides wrapper classes for each primitive type. For example, the Integer class wraps an int value, Double wraps a double, and Boolean wraps a boolean.

Before Java 5, programmers had to manually "box" primitives into their wrapper class objects. This code required verbose manual conversions:

ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(new Integer(10)); // Manual boxing
int firstNum = numbers.get(0).intValue(); // Manual unboxing

Use code with caution.

With the introduction of autoboxing and unboxing in Java 5, the compiler handles these conversions automatically. This allows you to write code that looks like you are directly adding and retrieving primitives, making the process much more seamless.

ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(10); // Autoboxing: `10` (int) is automatically wrapped in an `Integer` object
int firstNum = numbers.get(0); // Auto-unboxing: The `Integer` object is automatically converted to an `int`

Use code with caution.

2. Heterogeneous collections (without a common base type)

While an ArrayList can, in a technical sense, store heterogeneous data, it is not recommended and is considered bad practice. Storing completely unrelated objects together bypasses Java's type-safety features and can lead to ClassCastException at runtime.

The root of the issue is that ArrayList<T> is type-safe. When you declare ArrayList<String>, the compiler ensures only String objects can be added. For a heterogeneous collection, you would typically use a raw ArrayList (without a generic type) or ArrayList<Object>.

The problem with ArrayList<Object>

Using ArrayList<Object> defeats the purpose of generics and strong typing.

  • Type checks are necessary: Every time you retrieve an element, you must perform an instanceof check to verify its type before casting.
  • Runtime errors: If you forget to perform the type check, or perform the wrong one, you will encounter a ClassCastException at runtime.

Consider this example of what not to do:

ArrayList<Object> mixedList = new ArrayList<>();
mixedList.add("Hello");
mixedList.add(123); // Autoboxed to Integer
mixedList.add(new MyCustomClass());
// This will compile, but cause a runtime error
String s = (String) mixedList.get(1); // ClassCastException: Integer cannot be cast to String

Use code with caution.

The correct approach for related objects

If the objects you want to store are related through inheritance, you can use the most specific common superclass or interface as the generic type. This preserves type safety while allowing for polymorphism.

// Assuming Dog and Cat are subclasses of the Animal class
ArrayList<Animal> pets = new ArrayList<>();
pets.add(new Dog());
pets.add(new Cat());
// This is safe because all elements are guaranteed to be of type Animal
for (Animal pet : pets) {
    pet.makeSound();
}

Use code with caution.

Deeper dive into the "why"

Generics and type erasure

The fundamental reason an ArrayList can't store primitives is due to how Java handles generics. Generics were introduced to provide type safety at compile time. The compiler uses the generic type <T> to check that all elements added to the list are of the correct type.

However, this type information is "erased" during compilation. At runtime, ArrayList<String> and ArrayList<Integer> both become a raw ArrayList of Object. Since primitives are not objects and don't inherit from Object, they cannot exist in this structure.

Efficiency of primitives vs. objects

Java's performance is also tied to this distinction. Primitives are lightweight and are stored directly in memory, often on the stack, for fast access. Objects are stored on the heap, and a reference to the object is what's actually held. If an ArrayList were to store primitives, it would need a different internal structure to handle both types efficiently, which would defeat the design of a unified Collections Framework. The use of wrapper classes allows the ArrayList to maintain its object-only structure while providing the convenience of storing number values.

Enjoyed this article? Share it with a friend.