1 What is a Ragged Array?
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.
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:
- 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:
-
Read the first line to get the number of students (
numStudents). -
Create a 2D array of size
[numStudents][].Notice the empty second bracket[]- we don't know the column size yet! -
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]
\\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!
- Download both files to the same folder
- Open a terminal/command prompt in that folder
- Compile:
javac RaggedArrayReader.java - Run:
java RaggedArrayReader
5 Complete Java Code
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)
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.
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.
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)
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.
Scanner fileReader = new Scanner(new File("marks.txt"));This line does TWO things:
new File("marks.txt")- Creates a File object pointing to "marks.txt" in the current foldernew 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)
int numStudents = Integer.parseInt(fileReader.nextLine());This line does THREE things (read from inside out):
fileReader.nextLine()- Reads the entire first line as a String (e.g.,"4")Integer.parseInt(...)- Converts the String"4"to the integer4int numStudents = ...- Stores the result in variablenumStudents
numStudents = 4 (we have 4 students)
Part 4: Creating the Ragged Array (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 integersnew int[numStudents][]- Creates an array of 4 rows, but each row isnull(no columns yet!)
studentMarks[0] = nullstudentMarks[1] = nullstudentMarks[2] = nullstudentMarks[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)
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.
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.
String line = fileReader.nextLine().trim();This line does TWO things:
fileReader.nextLine()- Reads the entire next line (e.g.,"10 20").trim()- Removes spaces at the beginning and end
File line:
"10 20"After trim:
"10 20" (stored in line)
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
Input:
"10 20"Result:
tokens[0] = "10", tokens[1] = "20"tokens.length = 2
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!
If
tokens.length = 2, creates marks = new int[2]If
tokens.length = 5, creates marks = new int[5]
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.
tokens[0] = "10" → marks[0] = 10tokens[1] = "20" → marks[1] = 20
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.
studentMarks[0] = {10, 20} ← Now has data!studentMarks[1] = nullstudentMarks[2] = nullstudentMarks[3] = null
Part 6: Closing the File (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)
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).
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[1] = {9, 18, 30, 20, 10}: loops five times
Part 8: Exception Handling (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 | 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
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
Each student's marks are stored in a separate 1D array with exactly the right size. No wasted memory!
8 Key Takeaways
- 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 stringsplit("\\s+")breaks a string by whitespace into an arrayInteger.parseInt()converts a string to an integer- Assign each row individually:
array[i] = new int[size]