Arrays & Multidimensional Arrays
15 questions foundA sparse matrix stores only the non-default (non-zero) elements, commonly using a Map<Long, T> keyed by an encoded (row, column) pair, or a Map<Integer, Map<Integer, T>> for nested row-then-column lookup, or specialized formats like Compressed Sparse Row (CSR) for numerical computing -- this avoids allocating and holding memory for the potentially enormous number of default-valued cells a genuinely sparse dataset (like a large graph's adjacency matrix, or a mostly-empty spreadsheet) would otherwise require if represented as a dense 2D array.
public class SparseMatrix {
private final Map<Long, Double> data = new HashMap<>();
private final int cols;
public SparseMatrix(int cols) { this.cols = cols; }
private long key(int row, int col) { return (long) row * cols + col; }
public void set(int row, int col, double value) {
if (value == 0.0) data.remove(key(row, col));
else data.put(key(row, col), value);
}
public double get(int row, int col) {
return data.getOrDefault(key(row, col), 0.0);
}
}
Real-world example A recommendation engine's user-item interaction matrix (millions of users by millions of items, but each user has only interacted with a tiny fraction) is stored as a sparse Map-based structure rather than a dense 2D array, since a dense representation at that scale would require far more memory than any reasonable server could provide.
Common follow-ups: What's the performance trade-off of Map-based sparse storage versus a dense array for operations like matrix multiplication?;How do specialized sparse matrix libraries (like those used in scientific computing) further optimize beyond a simple HashMap-based approach?
Collections Framework;Diagnostics & Performance
Due to Java's type erasure, generic type information isn't available at runtime, so the JVM couldn't correctly enforce the component type of a genuinely generic array at the point of array creation -- combined with array covariance's already-demonstrated unsoundness (ArrayStoreException), allowing new T[] or new List<String>[] directly would create even more severe, undetectable type-safety holes, so Java simply disallows this syntax entirely at compile time, requiring workarounds like creating an Object[] and unsafely casting, or preferring a generic-friendly collection like ArrayList<T> instead.
public class Container<T> {
// private T[] items = new T[10]; // COMPILE ERROR: generic array creation
@SuppressWarnings("unchecked")
private T[] items = (T[]) new Object[10]; // common workaround, but unsafe and generates a warning
// Preferred alternative: just use a List instead
private List<T> betterItems = new ArrayList<>();
}
Real-world example A generic container class initially attempts private T[] items = new T[10] and hits a compile error, leading the team to either use the unsafe Object[]-cast workaround (accepting the unchecked warning) or, preferably, refactor to use ArrayList<T> internally instead, sidestepping the entire generic array limitation.
Common follow-ups: Why does the unsafe (T[]) cast workaround still risk a ClassCastException later, and under what circumstances?;How does the JDK's own source code (like ArrayList's internal implementation) handle this exact limitation internally?
Generics;Collections Framework
An array of primitives (int[], double[]) has elements automatically initialized to their type's zero-equivalent default (0, 0.0, false), while an array of reference types (String[], custom objects) has every element initialized to null by default, since no object instance is automatically created -- attempting to call a method on an uninitialized reference-type array element before explicitly assigning it throws a NullPointerException.
int[] nums = new int[3]; // {0, 0, 0}
boolean[] flags = new boolean[3]; // {false, false, false}
String[] names = new String[3]; // {null, null, null}
System.out.println(names[0].length()); // throws NullPointerException, since names[0] is null
Real-world example A beginner's attempt to call .length() on an element of a freshly created String[] array (without first assigning actual String values to each slot) throws a NullPointerException, a common early mistake stemming from not realizing reference-type arrays default to null rather than empty strings.
Common follow-ups: Why don't reference-type arrays default to an empty object instance instead of null?;How would you safely initialize every element of an object array to a non-null default value?
Java Fundamentals: Syntax Data Types & Operators;Exceptions
Flattening a 2D array involves iterating each row and copying its elements sequentially into a pre-sized 1D destination array (tracking a running offset via System.arraycopy() for efficiency); reshaping a 1D array into 2D requires knowing the desired row/column dimensions in advance and copying corresponding slices of the source array into each newly allocated row -- neither operation has a single built-in JDK utility method, so both are typically implemented manually using System.arraycopy() for performance.
public static int[] flatten(int[][] matrix) {
int totalLength = Arrays.stream(matrix).mapToInt(row -> row.length).sum();
int[] result = new int[totalLength];
int offset = 0;
for (int[] row : matrix) {
System.arraycopy(row, 0, result, offset, row.length);
offset += row.length;
}
return result;
}
Real-world example An image-processing routine flattens a 2D pixel grid into a single contiguous 1D array before passing it to a native image-encoding library expecting flat pixel data, using System.arraycopy() per row for efficient bulk copying rather than a slower element-by-element nested loop.
Common follow-ups: What is the time complexity of the flattening operation relative to the total number of elements?;How would you handle flattening a jagged (non-rectangular) 2D array where rows have different lengths?
Diagnostics & Performance;Collections Framework
A Java array is itself an object on the heap, carrying an object header (typically 12-16 bytes on a 64-bit JVM, including a mark word and class pointer) plus a 4-byte length field, followed by the actual element data laid out contiguously -- a primitive array (like int[]) stores raw values directly and compactly in this contiguous block, while an array of boxed wrapper types (Integer[]) instead stores references (pointers) to separately-heap-allocated Integer objects, meaning each element incurs both the pointer's own 4-8 bytes AND a separate object header for each boxed Integer instance, resulting in dramatically higher memory usage and worse cache locality for large numeric datasets.
// Primitive array: compact, contiguous memory, ~16 bytes header + 4 bytes per int
int[] primitiveArray = new int[1_000_000]; // roughly 4MB total
// Boxed array: each element is a separate heap object plus a reference
Integer[] boxedArray = new Integer[1_000_000]; // roughly 20MB+ total (references + individual Integer objects)
Real-world example A scientific computing application processing millions of numeric data points switches from using an Integer[] (or a generic List<Integer>) to a raw int[] array after profiling revealed the boxed representation was consuming roughly 4-5x more heap memory and causing significantly worse CPU cache performance due to pointer-chasing through scattered Integer objects.
Common follow-ups: How does the JIT compiler's escape analysis sometimes mitigate boxing overhead in tight loops?;What is the memory layout difference between a 2D primitive array and a flattened 1D equivalent?
Garbage Collection;Diagnostics & Performance