☕ Unit 4 • Core Java Fundamentals

Arrays and Strings: Memory Topologies & Text Processing

Comprehensive enterprise mastery of linear and multi-dimensional memory structures: 1D, 2D, and Jagged arrays, Heap allocation mechanics, pass-by-reference semantics, the java.util.Arrays toolkit, String immutability, the String Constant Pool (SCP), and mutable string builders (StringBuffer vs. StringBuilder) with high-performance formatting.

Curriculum: Enterprise Java Software Engineering
Unit: 04 of 15
Prerequisite: Unit 03 (Control Statements)
Target: JDK 21 LTS
01

Arrays in Java: Memory Allocation & Dimensions

📦 What is an Array in Java? An Object-Oriented View

In C/C++, an array is simply a contiguous chunk of raw memory addressed by pointers. In Java, an array is a dynamically created object allocated on the JVM Garbage-Collected Heap. An array holds a fixed number of values of a single specified data type (homogeneous elements):

  • Fixed Size: The capacity of an array is determined at instantiation time via new type[size] and cannot be altered dynamically.
  • Contiguous Heap Allocation: Elements are indexed from 0 up to array.length - 1. Accessing an illegal index throws an ArrayIndexOutOfBoundsException at runtime.
  • Built-in Field: Every Java array object features an immutable public final field: length (not a method).
📐 One-Dimensional, Two-Dimensional & Jagged Arrays

Java does not support true contiguous multi-dimensional matrices in flat physical memory. Instead, a multi-dimensional array is an "Array of Arrays":

  • 1D Array: int[] scores = new int[5]; — Stores 5 primitive integers contiguously in a single heap object.
  • 2D Array (Matrix): int[][] matrix = new int[3][4]; — A top-level array of length 3, where each cell stores a reference pointer to another separate array of length 4.
  • Jagged (Ragged) Array: Because sub-arrays are independent heap objects, each row can have a different length:
    int[][] jagged = new int[3][];
    jagged[0] = new int[2];
    jagged[1] = new int[5];
    jagged[2] = new int[3];
Visual Architecture: JVM Heap Topology of a Jagged Array (Array of Arrays)
Stack Reference
int[][] arr = @0x1000
➔
Row Pointer Array (@0x1000)
[0]: @0x2001 | [1]: @0x2002
▼ Heap Object References
Row 0 Array (@0x2001)
[10, 20] (Length: 2)
Row 1 Array (@0x2002)
[30, 40, 50, 60] (Length: 4)
02

Array Traversal, Method Passing & Command-Line Arguments

🔄 Passing Arrays to Methods: Pass-by-Value of the Reference

Java is strictly pass-by-value. When an array is passed as an argument to a method, a copy of the reference address is passed. Consequently:

  • Mutating array elements inside the method (arr[0] = 999;) modifies the caller's array directly because both references point to the same heap object.
  • Reassigning the array parameter (arr = new int[10];) rebinds only the local method reference and has no effect on the caller's reference.
💻 Command-Line Arguments: String[] args

The JVM captures arguments typed after the class name during terminal execution (java App user 8080) and populates the String[] args array. Arguments are indexed starting at args[0], and multiple arguments are space-delimited.

ArrayToolkitDemo.java — Array Manipulation & java.util.Arrays
import java.util.Arrays;

public class ArrayToolkitDemo {
    public static void main(String[] args) {
        int[] numbers = { 54, 12, 89, 23, 67 };

        // 1. Dual-Pivot Quicksort via Arrays.sort()
        Arrays.sort(numbers);
        System.out.println("Sorted Array     : " + Arrays.toString(numbers));

        // 2. Binary Search in O(log N) - Array MUST be sorted first!
        int searchTarget = 67;
        int foundIndex = Arrays.binarySearch(numbers, searchTarget);
        System.out.printf("Found %d at Index : %d%n", searchTarget, foundIndex);

        // 3. Array Copying & Filling
        int[] clonedSlice = Arrays.copyOf(numbers, 3);
        System.out.println("Cloned 3 Elements: " + Arrays.toString(clonedSlice));

        // 4. Multidimensional Deep String formatting
        int[][] grid = { { 1, 2 }, { 3, 4 } };
        System.out.println("Deep 2D Matrix   : " + Arrays.deepToString(grid));
    }
}
Execution Result
Sorted Array : [12, 23, 54, 67, 89] Found 67 at Index : 3 Cloned 3 Elements: [12, 23, 54] Deep 2D Matrix : [[1, 2], [3, 4]]
03

Strings in Java: The String Constant Pool & Immutability

🔒 The Architecture of String Immutability

In Java, the String class (defined in java.lang) is immutable: once a String object is instantiated on the Heap, its internal character sequence can never be altered. Any method that appears to modify a String (e.g., toUpperCase(), concat(), replace()) actually allocates and returns an entirely new String object in memory!

Why is String Immutable in Java? (Top Tier Viva Topic):

  • String Constant Pool (SCP) Optimization: Enables sharing of identical string literals, saving massive amounts of heap memory.
  • Security: Strings are used to store sensitive data like database URLs, usernames, passwords, and network sockets. If mutable, an unauthorized thread could alter the connection string after security verification.
  • Thread Safety: Because immutable objects cannot change their internal state, String instances can be shared freely across concurrent threads without synchronization locks.
  • HashCode Caching: The hash code of a String is computed once and cached lazily. This makes Strings the ideal, ultra-fast key for hash collections like HashMap and HashSet.
🏊 String Creation: Literal vs. new String()
  • String Literal (String s = "Java";): The JVM checks the String Constant Pool (SCP) located inside the Heap. If "Java" already exists in the pool, no new object is created; the JVM simply returns a reference to the existing pooled instance.
  • Using new Keyword (String s = new String("Java");): The JVM unconditionally creates a new object on the general Heap outside the SCP, even if the identical literal already exists in the SCP.
Visual Architecture: String Constant Pool (SCP) vs. Normal Heap Memory
Stack Pointers
s1 = "Java"
s2 = "Java"
s3 = new String("Java")
────── References ──────➔
String Constant Pool (SCP)
@0xPOOL_1 ["Java"]
(Both s1 & s2 point HERE)
────── Reference ──────➔
General Heap Object
@0xHEAP_9 ["Java"]
(s3 points HERE)
Result: (s1 == s2) is TRUE | (s1 == s3) is FALSE | s1.equals(s3) is TRUE
04

String Comparison & Essential String Methods

⚖️ String Comparison: == vs. equals() vs. compareTo()
  • == Operator: Compares reference addresses in memory. Returns true only if both references point to the exact same memory location.
  • .equals(): Compares the actual content/characters for semantic equality. Case-sensitive.
  • .equalsIgnoreCase(): Compares string contents while ignoring upper/lower case differences.
  • .compareTo(): Compares two strings lexicographically based on Unicode values. Returns 0 if equal, negative if calling string precedes argument, and positive if it follows.
Method Signature Return Type Description & Behavior
length() int Returns total number of UTF-16 code units in the string.
charAt(int index) char Returns the character at the specified 0-based index.
substring(int start, int end) String Extracts substring from start (inclusive) to end (exclusive).
contains(CharSequence s) boolean Returns true if the string contains the specified sequence.
split(String regex) String[] Splits string into array of tokens around regular expression matches.
strip() / trim() String Removes leading and trailing whitespace (strip() is Unicode-aware, Java 11+).
replace(char old, char new) String Returns new string replacing all occurrences of old character with new.
05

Mutable Strings: StringBuffer vs. StringBuilder

⚡ Why Mutable Strings Matter: The Loop Concatenation Anti-Pattern

Because Strings are immutable, concatenating strings in a loop using += is a severe performance anti-pattern. Each iteration creates an entirely new temporary String object and discards the previous one, leading to $O(N^2)$ time complexity and aggressive heap memory churn!

Java provides two mutable companion classes that maintain a growable internal buffer (`char[]` / `byte[]`):

  • StringBuffer (Java 1.0): Thread-Safe & Synchronized. Every public method (append(), insert(), delete()) is synchronized with monitor locks. Safe for concurrent multi-threaded writes, but incurs significant lock acquisition overhead.
  • StringBuilder (Java 5): Not Thread-Safe & Non-Synchronized. Identical API to StringBuffer, but without synchronization overhead. Designed for single-threaded operations and local method buffers. Always use StringBuilder unless thread-safety across multiple threads is explicitly required!
Characteristic String StringBuffer StringBuilder
Mutability Immutable Mutable Mutable
Thread-Safety Naturally Thread-Safe Thread-Safe (Synchronized) Not Thread-Safe
Performance Speed Slow on repeated modifications Medium (Lock overhead) Blazing Fast (No locks)
Memory Location String Constant Pool / Heap Heap Heap
Introduction Java 1.0 Java 1.0 Java 5.0 (Modern Standard)
06

String Formatting: String.format() & Printf Specifiers

🖨️ Precision Output with Format Specifiers

Java provides String.format() and System.out.printf() for generating structured, aligned, and localized string presentations. Format specifiers follow the syntax:
%[argument_index$][flags][width][.precision]conversion_character

  • %s : Formats strings or arbitrary objects.
  • %d : Formats decimal integers.
  • %f : Formats floating-point numbers (e.g., %.2f rounds to 2 decimal places).
  • %10s : Right-aligns string in a field width of 10 spaces.
  • %-10s : Left-aligns string in a field width of 10 spaces.
  • %,d : Inserts locale-specific thousands grouping commas (e.g., 1,000,000).
  • %n : Platform-independent newline separator (preferable to \n).
07

Unit 4 Practical Capstone: Text Analytics & Matrix Processing Engine

🚀 Practical Engineering Project

To synthesize Unit 4, we build an end-to-end Text Analytics and Matrix Transformation Engine that demonstrates: 2D Jagged array allocations, Arrays.sort() and binary search, parsing raw delimited text records, string cleaning, high-speed StringBuilder token reconstruction, and formatted table generation.

TextAndMatrixEngine.java — Complete Capstone Implementation
import java.util.Arrays;

/**
 * DevAscent Academy — Unit 4 Capstone Project
 * Demonstrates 2D Arrays, String Pool Dynamics, and StringBuilder Processing.
 */
public class TextAndMatrixEngine {

    public static void main(String[] args) {
        System.out.println("==================================================");
        System.out.println("   DEVASCENT TEXT ANALYTICS & MATRIX PIPELINE    ");
        System.out.println("==================================================");

        // 1. Parsing & Transforming Delimited Data via StringBuilder
        String rawLog = "  prod-us-101 | 240ms | OK ; prod-eu-202 | 180ms | OK ; prod-ap-303 | 920ms | TIMEOUT  ";
        String[] serverRecords = rawLog.trim().split("\\s*;\\s*");

        System.out.println("--- 1. Parsed Server Telemetry Report ---");
        System.out.printf("%-14s %-10s %-10s%n", "SERVER ID", "LATENCY", "STATUS");
        System.out.println("----------------------------------------");

        StringBuilder alertSummary = new StringBuilder();

        for (String record : serverRecords) {
            String[] tokens = record.split("\\s*\\|\\s*");
            String serverId = tokens[0];
            String latency  = tokens[1];
            String status   = tokens[2];

            System.out.printf("%-14s %-10s %-10s%n", serverId, latency, status);

            if (!status.equalsIgnoreCase("OK")) {
                alertSummary.append("[ALERT: ").append(serverId).append(" -> ").append(status).append("] ");
            }
        }

        System.out.println("Incident Report : " + (alertSummary.length() > 0 ? alertSummary.toString() : "All nodes healthy"));
        System.out.println("--------------------------------------------------");

        // 2. Jagged Sensor Array: Varying Readings per Station
        System.out.println("--- 2. Jagged Sensor Matrix Analysis ---");
        double[][] sensorReadings = new double[3][];
        sensorReadings[0] = new double[] { 23.4, 24.1 };             // Station 0: 2 readings
        sensorReadings[1] = new double[] { 19.8, 20.1, 19.5, 20.8 }; // Station 1: 4 readings
        sensorReadings[2] = new double[] { 31.2, 32.0, 31.8 };       // Station 2: 3 readings

        for (int i = 0; i < sensorReadings.length; i++) {
            double sum = 0.0;
            for (double temp : sensorReadings[i]) {
                sum += temp;
            }
            double avg = sum / sensorReadings[i].length;
            System.out.printf("Station #%d (%d readings): Avg Temp = %.2f°C | Raw = %s%n",
                    i, sensorReadings[i].length, avg, Arrays.toString(sensorReadings[i]));
        }
        System.out.println("==================================================");
    }
}
Execution Result (javac & java)
$ javac TextAndMatrixEngine.java $ java TextAndMatrixEngine ================================================== DEVASCENT TEXT ANALYTICS & MATRIX PIPELINE ================================================== --- 1. Parsed Server Telemetry Report --- SERVER ID LATENCY STATUS ---------------------------------------- prod-us-101 240ms OK prod-eu-202 180ms OK prod-ap-303 920ms TIMEOUT Incident Report : [ALERT: prod-ap-303 -> TIMEOUT] -------------------------------------------------- --- 2. Jagged Sensor Matrix Analysis --- Station #0 (2 readings): Avg Temp = 23.75°C | Raw = [23.4, 24.1] Station #1 (4 readings): Avg Temp = 20.05°C | Raw = [19.8, 20.1, 19.5, 20.8] Station #2 (3 readings): Avg Temp = 31.67°C | Raw = [31.2, 32.0, 31.8] ==================================================
08

University Exam & Technical Interview Viva Preparation Guide

Q1. What is the String Constant Pool (SCP) and where does it reside in memory?
The String Constant Pool (SCP) is a dedicated cache region within the JVM Heap (moved from PermGen to Heap in Java 7). When a String literal is created, the JVM checks the SCP first. If an identical literal already exists, the reference to the pooled object is returned instead of allocating duplicate memory. This optimization drastically reduces the heap footprint of repetitive strings.
Q2. What is the output of String s1 = "Java"; String s2 = new String("Java"); System.out.println(s1 == s2);?
The output is false. The == operator compares reference memory addresses. s1 references an object residing inside the String Constant Pool, whereas s2 was explicitly created using new, placing it in the general Heap memory outside the SCP. Since they occupy different memory addresses, s1 == s2 is false. However, s1.equals(s2) evaluates to true because their character contents are identical.
Q3. Why should StringBuilder be preferred over String when concatenating in loops?
Because String objects are immutable, using += inside a loop creates a new String object in every iteration and discards the old one, leading to $O(N^2)$ algorithmic time complexity and heavy garbage collection overhead. StringBuilder maintains a resizable internal buffer that appends characters in-place with $O(1)$ amortized time complexity, running orders of magnitude faster.
Q4. What is the difference between StringBuffer and StringBuilder?
StringBuffer is thread-safe and synchronized: its public methods acquire monitor locks, making it safe for concurrent access across multiple threads, but slower due to synchronization overhead. StringBuilder is non-synchronized and not thread-safe, making it significantly faster. StringBuilder is the modern standard for single-threaded or method-local string manipulation.
Q5. What is a Jagged Array in Java and how is it constructed?
A Jagged (or Ragged) Array is a multi-dimensional array where each sub-array (row) has a different number of columns. Because Java implements multi-dimensional arrays as an "Array of Arrays", each row is an independent heap object. It is constructed by instantiating the row dimension first (int[][] arr = new int[3][];) and then instantiating each row individually with custom lengths (arr[0] = new int[2]; arr[1] = new int[5];).
Q6. What happens if you pass an array to a method and modify its elements?
Java passes the reference by value (a copy of the memory pointer). The method's local parameter points to the exact same array object on the Heap as the caller. Therefore, modifying elements (e.g., arr[0] = 99;) mutates the caller's array directly. However, reassigning the parameter reference itself (arr = new int[5];) changes only the local method reference and has no effect on the caller's array.
Q7. What is the difference between length and length() in Java?
length is a public final field (attribute) defined on array objects that returns the fixed capacity of the array (e.g., myArray.length). length() is a public method defined on the String class that returns the number of UTF-16 characters present in the string (e.g., myString.length()).
Q8. What does the String.intern() method do?
The intern() method searches the String Constant Pool for a string with the exact same character content as the calling String object. If found, it returns the reference from the pool. If not found, it adds the string to the pool and returns a reference to it. This allows a heap string created via new to be deduplicated and normalized against the SCP.