Reading Ragged Arrays from a File

Working with Variable-Length Data in Java

CPCS203 - Data Structures

1 What is a Ragged Array?

Ragged Array Definition

A ragged array (also called a jagged array) is a 2D array where each row can have a different number of columns. This is perfect for real-world data like student marks where different students may have taken different numbers of courses!

In this tutorial, we'll learn how to read a ragged array of student marks from a text file. Each student may have a different number of marks, making this a perfect use case for ragged arrays.

Example: Ragged Array of Student Marks
Student 0:
10
20
Student 1:
9
18
30
20
10
Student 2:
10
15
Student 3:
5
20
26
Why Ragged Arrays?

Notice how each student has a different number of marks. Student 1 has 5 marks while Students 0 and 2 have only 2. A regular 2D array would waste memory by allocating the same size for all rows!

2 File Format Overview

Our input file marks.txt has a simple structure:

marks.txt
4 // Number of students
10 20 // Student 0: 2 marks
9 18 30 20 10 // Student 1: 5 marks
10 15 // Student 2: 2 marks
5 20 26 // Student 3: 3 marks
File Structure Explained
  • The first line contains the number of students (4)
  • Each subsequent line contains a variable number of marks for that student
  • Marks on each line are separated by spaces

3 Reading Algorithm: Step by Step

We'll use nextLine() and split() to read and parse the file:

  1. Read the first line to get the number of students (numStudents).
  2. Create a 2D array of size [numStudents][].
    Notice the empty second bracket [] - we don't know the column size yet!
  3. For each remaining line:
    • Read the entire line as a string
    • Split on whitespace using split("\\s+")
    • Convert each token to int
    • Assign the resulting 1D array to studentMarks[i]
What is \\s+?

The pattern \\s+ is a regular expression that matches one or more whitespace characters (spaces, tabs, etc.). This handles cases where numbers might be separated by multiple spaces.

4 Download Files

Download both files to run the example. Place them in the same folder!

How to Run
  1. Download both files to the same folder
  2. Open a terminal/command prompt in that folder
  3. Compile: javac RaggedArrayReader.java
  4. Run: java RaggedArrayReader

5 Complete Java Code

RaggedArrayReader.java
 1// RaggedArrayReader.java
 2import java.io.File;
 3import java.io.FileNotFoundException;
 4import java.util.Scanner;
 5
 6public class RaggedArrayReader {
 7    public static void main(String[] args) {
 8        try {
 9            // Create Scanner to read from file
10            Scanner fileReader = new Scanner(new File("marks.txt"));
11
12            // Step 1: Read number of students from the first line
13            int numStudents = Integer.parseInt(fileReader.nextLine());
14
15            // Step 2: Create a ragged array (only first dimension specified!)
16            int[][] studentMarks = new int[numStudents][];
17
18            // Step 3: For each student, read their line of marks
19            for (int i = 0; i < numStudents; i++) {
20                // Check if there's another line to read
21                if (!fileReader.hasNextLine()) {
22                    System.err.println("Not enough lines for " + numStudents + " students!");
23                    break;
24                }
25
26                // Read the entire line of marks
27                String line = fileReader.nextLine().trim();
28
29                // Split by one or more spaces (regex: \s+)
30                String[] tokens = line.split("\\s+");
31
32                // Convert string tokens to integers
33                int[] marks = new int[tokens.length];
34                for (int j = 0; j < tokens.length; j++) {
35                    marks[j] = Integer.parseInt(tokens[j]);
36                }
37
38                // Assign this 1D array to the ragged array
39                studentMarks[i] = marks;
40            }
41
42            // Close the file
43            fileReader.close();
44
45            // Print the ragged array to verify
46            System.out.println("Ragged array of student marks:");
47            for (int i = 0; i < studentMarks.length; i++) {
48                System.out.print("Student " + (i + 1) + ": ");
49                for (int mark : studentMarks[i]) {
50                    System.out.print(mark + " ");
51                }
52                System.out.println();
53            }
54
55        } catch (FileNotFoundException e) {
56            System.err.println("File not found: " + e.getMessage());
57        }
58    }
59}

6 Code Explanation - Line by Line

Let's go through every line of the code so you understand exactly how to write it yourself!

Part 1: Import Statements (Lines 2-4)

Line 2: import java.io.File;

What it does: Imports the File class from Java's I/O library.

Why we need it: The File class represents a file on your computer. We use it to tell the Scanner which file to read.

Line 3: import java.io.FileNotFoundException;

What it does: Imports the FileNotFoundException exception class.

Why we need it: When we try to open a file, it might not exist! Java forces us to handle this possibility using try-catch.

Line 4: import java.util.Scanner;

What it does: Imports the Scanner class.

Why we need it: Scanner is our tool for reading data. We've used it with System.in for keyboard input - now we'll use it with a File to read from a file!

Part 2: Opening the File (Lines 8-10)

Line 8: try {

What it does: Starts a try block for exception handling.

Why we need it: File operations can fail (file doesn't exist, no permission, etc.). The try block lets us attempt the operation and handle errors gracefully.

Line 10: Scanner fileReader = new Scanner(new File("marks.txt"));

This line does TWO things:

  1. new File("marks.txt") - Creates a File object pointing to "marks.txt" in the current folder
  2. new Scanner(...) - Creates a Scanner that reads from that File

Think of it like: "Open the file marks.txt and give me a Scanner to read it"

Part 3: Reading Number of Students (Line 13)

Line 13: int numStudents = Integer.parseInt(fileReader.nextLine());

This line does THREE things (read from inside out):

  1. fileReader.nextLine() - Reads the entire first line as a String (e.g., "4")
  2. Integer.parseInt(...) - Converts the String "4" to the integer 4
  3. int numStudents = ... - Stores the result in variable numStudents
After this line: numStudents = 4 (we have 4 students)

Part 4: Creating the Ragged Array (Line 16)

Line 16: int[][] studentMarks = new int[numStudents][];

What it does: Creates a 2D array with only the FIRST dimension specified!

Breaking it down:

  • int[][] - Declares a 2D array of integers
  • new int[numStudents][] - Creates an array of 4 rows, but each row is null (no columns yet!)
Memory visualization after this line:
studentMarks[0] = null
studentMarks[1] = null
studentMarks[2] = null
studentMarks[3] = null

Why empty second dimension? Because we don't know yet how many marks each student has! We'll fill in each row when we read it.

Part 5: The Main Loop (Lines 19-40)

Line 19: for (int i = 0; i < numStudents; i++) {

What it does: Loops through each student (0, 1, 2, 3).

Each iteration: Reads one line from the file and creates one row in our ragged array.

Lines 21-24: Error Checking

if (!fileReader.hasNextLine()) { ... break; }

What it does: Checks if there's another line to read.

Why we need it: Safety check! If the file has fewer lines than expected, we stop gracefully instead of crashing.

Line 27: String line = fileReader.nextLine().trim();

This line does TWO things:

  1. fileReader.nextLine() - Reads the entire next line (e.g., "10 20")
  2. .trim() - Removes spaces at the beginning and end
Example (first iteration i=0):
File line: "10 20"
After trim: "10 20" (stored in line)
Line 30: String[] tokens = line.split("\\s+");

What it does: Splits the string by whitespace into an array of strings.

The regex \\s+ means:

  • \\s - Any whitespace character (space, tab, etc.)
  • + - One or more of them
Example:
Input: "10 20"
Result: tokens[0] = "10", tokens[1] = "20"
tokens.length = 2
Line 33: int[] marks = new int[tokens.length];

What it does: Creates a new 1D integer array with the EXACT size needed.

Why tokens.length? The number of tokens tells us how many marks this student has!

Example:
If tokens.length = 2, creates marks = new int[2]
If tokens.length = 5, creates marks = new int[5]
Lines 34-36: Converting Strings to Integers

for (int j = 0; j < tokens.length; j++) {
    marks[j] = Integer.parseInt(tokens[j]);
}

What it does: Loops through each token string and converts it to an integer.

Example:
tokens[0] = "10"marks[0] = 10
tokens[1] = "20"marks[1] = 20
Line 39: studentMarks[i] = marks;

What it does: Assigns our newly created 1D array to row i of the ragged array.

This is the KEY step! We're replacing null with an actual array.

After first iteration (i=0):
studentMarks[0] = {10, 20} ← Now has data!
studentMarks[1] = null
studentMarks[2] = null
studentMarks[3] = null

Part 6: Closing the File (Line 43)

Line 43: fileReader.close();

What it does: Closes the file and releases system resources.

Why important: Always close files when done! Leaving files open can cause memory leaks and prevent other programs from accessing the file.

Part 7: Printing the Results (Lines 46-53)

Line 47: for (int i = 0; i < studentMarks.length; i++) {

What it does: Loops through each student (each row).

studentMarks.length gives us the number of rows (students).

Line 49: for (int mark : studentMarks[i]) {

What it does: Enhanced for-loop that iterates through each mark in student i's array.

Why enhanced for-loop? Each row has different length! The enhanced for-loop automatically handles this.

For studentMarks[0] = {10, 20}: loops twice
For studentMarks[1] = {9, 18, 30, 20, 10}: loops five times

Part 8: Exception Handling (Lines 55-57)

Lines 55-57: catch (FileNotFoundException e) { ... }

What it does: Catches the exception if the file doesn't exist.

What happens: If "marks.txt" is not found, the program prints an error message instead of crashing.

e.getMessage() gives details about what went wrong.

Complete Execution Trace

Step-by-Step Execution with marks.txt
Step Action Result
1 Read "4" numStudents = 4
2 Create array studentMarks = [null, null, null, null]
3 i=0: Read "10 20" studentMarks[0] = {10, 20}
4 i=1: Read "9 18 30 20 10" studentMarks[1] = {9, 18, 30, 20, 10}
5 i=2: Read "10 15" studentMarks[2] = {10, 15}
6 i=3: Read "5 20 26" studentMarks[3] = {5, 20, 26}

7 Expected Output

Console Output
Ragged array of student marks:
Student 1: 10 20
Student 2: 9 18 30 20 10
Student 3: 10 15
Student 4: 5 20 26
Success!

Each student's marks are stored in a separate 1D array with exactly the right size. No wasted memory!

8 Key Takeaways

Summary
  • Ragged arrays allow different row sizes - perfect for variable-length data
  • Use new int[n][] to create an array with only the first dimension specified
  • nextLine() reads an entire line as a string
  • split("\\s+") breaks a string by whitespace into an array
  • Integer.parseInt() converts a string to an integer
  • Assign each row individually: array[i] = new int[size]