Java Essentials – Arrays (Module 2 Segment)
Recap of Previous Module
Covered background & history of Java, its key features, and wrote first basic Java programs.
Concluded with control statements (selection & iteration). These are foundational for today’s discussion because iteration is critical when working with arrays.
Learning Objectives for This Module
Implement single- and multi-dimensional arrays.
Understand declaration, definition, and invocation of functions (by value & by reference). (Functions proper will be addressed later in the module; today’s transcript focuses on arrays.)
Method overloading,
String, andStringBufferclasses will follow after arrays & functions.
Why Arrays?
Analogy: John & Daisy must process a large U.S. election data set (votes for Democratic vs. Republican parties across many states).
Primitive variables (
int,char, etc.) hold single values → not scalable.Arrays act as containers / data structures for sequential, homogeneous data, simplifying storage & traversal.
Practical outcome: Correct array use earned John a promotion → illustrates professional value of mastering data structures.
Core Characteristics of Arrays
Fixed-size contiguous block of memory holding same data type.
Indexing starts at and ends at where = total number of elements.
Array variable stores a reference (on stack); actual elements reside in heap.
Support both primitive and reference (non-primitive) types. Arrays in Java support both primitive and reference (non-primitive) types because although the array variable itself stores a reference on the stack, the actual elements reside in the heap. When an array holds primitive types (like `int` or `char`), the heap memory block allocated for the array directly stores the values of these primitive types. However, when an array holds reference types (like `String` objects or instances of custom classes), the heap memory block for the array stores references (memory addresses) that point to the actual objects, which are themselves stored elsewhere in the heap.
Advantages
Eliminates repetitive variable declarations.
Simplifies traversal (can use loops instead of one-by-one access).
Deterministic memory layout ⇒ predictable access time.
Declaring & Initialising One-Dimensional Arrays
Implicit size specification
int[] a = new int[5]; // indices: 0-4
Brackets after variable (same effect)
int a[] = new int[5];
Inline initialisation (implicit size)
int[] a = {1, 2, 3, 4, 5}; // size inferred as 5
Explicit, separate steps
int[] a; // declaration
a = new int[]{1,2,3}; // explicit creation & assignment
Basic Operations Demo
Attempting to store multiple ints in a single
int d = {…}raises compile-time error.Printing array variable directly shows reference value (e.g.,
[I@15db9742), not elements.Correct element access →
a[index].Looping to print all elements:
for(int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
Traversal & Control Statements
Used
forloop with counterito iterate sequential indices.Recalled prior module’s control flow concepts (iteration).
Multi-Dimensional Arrays
Needed when data has more than one varying dimension (e.g., height × depth, or votes per state per party).
Java stores multi-dimensional arrays as arrays of arrays.
Declaration Examples
int[][] m = new int[2][2]; // 2x2 matrix
char[][] c = new char[3][2]; // 3 rows, 2 columns
float[][] f = new float[5][2]; // 5x2 matrix
3×3 literal initialisation:
int[][] a = {
{1,2,3},
{4,5,6},
{7,8,9}
};
Traversing 2-D Array (Nested Loops)
for(int i = 0; i < a.length; i++) { // rows
for(int j = 0; j < a[i].length; j++) { // columns
System.out.print(a[i][j] + " ");
}
System.out.println(); // new row
}
Outer loop iterates rows; inner loop iterates columns.
Output appears in matrix form.
Memory & Index Mapping
Element at row , column accessed via
a[i][j].Total elements in matrix = .
Stack vs Heap Discussion
Stack: stores reference variable (
a).Heap: stores actual array objects and their contained values.
Printing
awithout index yields heap address string, not contents.
Both the Stack and the Heap are memory locations. The Stack is used to store the reference variable, which holds the memory address pointing to where the actual data is stored. The Heap is where the actual array objects and their contained values or the objects themselves reside.
In Java, both the Stack and the Heap are memory areas used for data storage, but they serve different purposes:
Stack Memory: This is primarily used for storing local variables, method call parameters, and references to objects. It is known for its Last-In, First-Out (LIFO) behavior. When a method is called, a new block (stack frame) is created on the stack for that method's variables, and when the method finishes, the block is popped off. Variables stored on the Stack are typically short-lived.
Heap Memory: This is where all actual objects and arrays are stored. When you create an object or an array using the
newkeyword, the memory for that object/array is allocated in the Heap. References to these Heap-allocated objects are then stored on the Stack (for local variables) or within other objects (for instance variables). The Heap is much larger and objects here can live for the entire duration of the application, as long as there are active references to them; otherwise, they become eligible for garbage collection.
In essence, reference variables (which hold memory addresses) sit on the Stack, pointing to the actual data (objects and arrays, including their primitive attributes) which reside in the Heap.
Java separates memory into Stack and Heap for several key reasons, primarily to optimize memory management, control data lifetime, and enhance flexibility:
Efficient Management of Local Data: The Stack is designed for fast, automatic allocation and deallocation of local variables and method call information. Its Last-In, First-Out (LIFO) nature makes operations extremely quick, as memory is simply added or removed from the top of the stack as methods are called and return.
Dynamic Object Storage: The Heap provides a space for dynamic memory allocation, meaning objects and arrays (whose sizes might not be known at compile time and whose lifetimes are not tied to a specific method's execution) can be created and stored. Objects in the Heap persist as long as there are active references pointing to them, allowing them to be shared across different methods or live for the application's duration.
Garbage Collection: The Heap is where Java's automatic garbage collector operates. By keeping objects separate from the stack's local variables, the JVM can efficiently identify and reclaim memory from unreferenced objects in the Heap, reducing memory leaks and simplifying development.
Flexibility and Predictability: This separation allows for more flexible object management on the Heap while maintaining highly predictable and fast access for local variables and execution flow on the Stack. Each thread typically has its own stack, but they share the heap, which is crucial for concurrency.
For a local variable declaration like int a = 5;, the primitive value 5 is typically stored directly on the Stack. So, in that sense, a (and its value) does reside on the Stack.
However, when we describe Stack variables as "short-lived" it's in contrast to Heap objects. The lifetime of a is indeed limited to the execution of the method in which it's declared. Once that method finishes, the stack frame is popped, and a (along with its value) is deallocated.
So, while the int value itself is on the Stack, its longevity is tied directly to the method's scope, making it short-lived compared to objects on the Heap which can persist as long as there are references to them, even across multiple method calls.
The reference variable on the Stack does not necessarily live as long as the object it points to in the Heap. Their lifetimes are governed by different rules:
Lifetime of a Reference on the Stack: A reference variable created on the Stack (e.g.,
Person person1;inside a method) lives only as long as the method call is active. Once the method finishes executing, its stack frame is popped, and all local variables (including reference variables) within that frame are deallocated.Lifetime of an Object on the Heap: An object created on the Heap lives as long as there is at least one active reference pointing to it. If all references to an object are gone (e.g., the stack reference that pointed to it went out of scope, or was reassigned, and no other references exist), the object becomes eligible for garbage collection. The garbage collector will eventually reclaim the memory occupied by that object.
So, while a stack reference enables you to access an object on the Heap, the object can continue to exist even after that specific stack reference is gone, provided other
How Java access data from the heap for Arrays
When you declare and initialize an array in Java using the new keyword (e.g., int[] a = new int[5];), the array object itself is created and stored in the Heap. This allocation reserves a contiguous block of memory in the Heap:
For arrays of primitive types (like
int,char,float): The actual primitive values are stored directly within this contiguous memory block in the Heap.For arrays of reference types (like
String[],Object[]): The Heap block stores references (memory addresses) to the actual objects. These objects themselves are also stored elsewhere in the Heap.
When you access an array element using an index, for example a[i]:
Reference Retrieval: Java first looks at the array variable
aon the Stack. This variable contains a memory address (a reference) that points to the beginning of the array object in the Heap.Offset Calculation: Using this base memory address from the Heap, Java calculates the exact memory location of the desired element. It does this by taking the base address and adding an offset, which is computed as
index * size_of_element. For example, for anintarray, ifiis 2, and eachinttakes 4 bytes, it moves 2 * 4 = 8 bytes from the start of the array in the Heap to find the element at index 2.Direct Access: Because this calculation is a simple arithmetic operation that directly yields the memory address, accessing an array element by its index in Java is an extremely fast, constant-time operation (O(1)O(1)). Java then retrieves the value stored at that specific memory location in the Heap.
How Java access data from the heap for Objects
In Java:
Objects themselves are always stored in the Heap.
References to objects (variables that point to an object's location in memory) are stored on the Stack (if they are local variables or method parameters) or within other objects in the Heap (if they are instance variables).
Let's say you have a Person object with an int attribute for age:
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
}
// In your main method or another method:
Person person1 = new Person("Alice", 30);
int aliceAge = person1.getAge();
Here's how Java retrieves the int attribute (age) from the Heap:
Reference on the Stack: The
person1variable is created on the Stack. It doesn't hold thePersonobject itself, but rather a memory address (a reference) pointing to where the actualPersonobject is located in the Heap.Object in the Heap: When
new Person("Alice", 30)is executed, a newPersonobject is allocated space in the Heap. ThisPersonobject's memory block within the Heap contains:The
nameattribute: This would be a reference to aStringobject ("Alice") which is also stored elsewhere in the Heap.The
ageattribute: Sinceageis a primitiveint, its actual value (3030) is stored directly within thePersonobject's memory block in the Heap. It's not a separate object or reference.
Retrieval Process (e.g.,
person1.getAge()orperson1.ageif public):Java first looks at the
person1variable on the Stack to get the memory address of thePersonobject in the Heap.It then navigates to that memory location in the Heap.
Once at the
Personobject's location in the Heap, Java knows the memory layout of aPersonobject (i.e., where each of its attributes is stored relative to the object's starting address). It directly accesses the specific offset within thatPersonobject's memory block where theage(integer) value is stored.The
intvalue (30) is then retrieved directly from that location in the Heap.
So, while the person1 variable (the reference) sits on the Stack, the actual Person object and its primitive attributes (like int age) reside together in a contiguous block of memory on the Heap. Java uses the
Theory vs Practice Summary
Single-Dimensional
Suitable when data varies along one axis.
Indexing sufficient with one counter.
Multi-Dimensional
Suitable for tabular or grid data (e.g., coordinates).
Requires nested loops for traversal.
Both
Support primitive & reference data types.
Provide time-saving, space-efficient alternative to many discrete variables.
Sample Concept-Check Questions & Answers
Q: How many bytes for
int x[][] = new int[5][5];?Elements = .
Each
int= bytes.Total = bytes.
Q: Can multi-dimensional operations be mimicked with single-dimensional arrays?
Yes, by manual index calculations (e.g., ).
But multi-dimensional arrays reduce coding, time, and conceptual overhead.
Key Takeaways
Arrays are foundational in Java for managing collections of homogeneous data.
Understand declaration syntaxes, memory model, and traversal techniques.
Multi-dimensional arrays extend capability to model more complex data structures with minimal additional syntax.
Proper use of arrays not only improves code clarity but can significantly affect program performance and developer productivity—just as John’s promotion story illustrates.
(Functions, method overloading, and advanced string handling will be covered in subsequent segments of the module.)