Arrays and Strings in Java (with StringBuilder)
Executive Summary
Among arrays and strings in Java, arrays are the fixed-length, zero-indexed containers of one type, with a public length field and element defaults of zero, false, or null. The java.util.Arrays class supplies copyOf, sort, fill, and toString, and contents are compared with Arrays.equals, never ==. String is immutable: every transformation returns a new object, so capture results and compare with equals. Concatenation with + is fine occasionally but quadratic inside loops; use StringBuilder there, or String.join for separators. Choose an array when the count is fixed and a List when it grows, which the collections articles in Part 3 cover in depth. Master these three tools and the practice builds, from the calculator to the CSV analyzer, stop producing surprises.
Arrays in Java: Fixed-Length and Zero-Indexed
An array holds a fixed number of values of one type. In other words, the length is set at creation and never changes; growing data needs a new array or, better, a List. Elements start at index 0 and end at length – 1, and the runtime checks every access, as the official arrays lesson explains.
int[] scores = new int[3]; // [0, 0, 0]: type defaults
int[] initialized = {72, 88, 95}; // literal form
String[] names = {"Ada", "Linus", "Grace"};
initialized[1] = 90; // write
int first = initialized[0]; // read: 72
System.out.println(initialized.length); // 3: a field, not a method
Also, new arrays fill with type defaults: 0 for numbers, false for booleans, null for references. Iteration uses the loops you already know:
public class ArrayBasics {
public static void main(String[] args) {
int[] scores = {72, 88, 95};
String[] names = {"Ada", "Linus", "Grace"};
for (int i = 0; i < scores.length; i++) {
System.out.println(i + ": " + scores[i]);
}
for (String name : names) {
System.out.println("hello, " + name);
}
}
}
The Arrays Utility Class
java.util.Arrays is the toolbox for array work, and four calls cover daily needs:
int[] a = {3, 1, 2};
int[] copy = java.util.Arrays.copyOf(a, a.length); // independent copy
java.util.Arrays.sort(a); // a is now [1, 2, 3]
System.out.println(java.util.Arrays.toString(a)); // [1, 2, 3]
boolean same = java.util.Arrays.equals(a, copy); // false: contents differ
Two-dimensional arrays are arrays of arrays, useful for grids: int[][] grid = new int[3][3];. In practice, most production grids become Lists of Lists, so treat 2D arrays as a language feature you can read when it appears.
Note the last line above: == on arrays compares references, not contents. Arrays.equals(a, b) is the comparison you want, and forgetting it produces a bug that reads correctly in every review.
Strings in Java: Immutable by Design
A String is a read-only sequence of characters. In fact, no method changes it; every operation returns a new String with the answer inside. Immutability is a feature, not an inconvenience: strings are safe to share, safe to cache as map keys, and free to hash.
However, immutability has one consequence everyone hits once. Wrong code first:
// WRONG: the result is discarded
String name = "java";
name.toUpperCase();
System.out.println(name); // java: nothing changed
// WRONG: == compares references, not characters
String a = "hello";
String b = new String("hello");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true: this is the comparison you want
// RIGHT: capture the returned value, compare with equals
String upper = name.toUpperCase();
System.out.println(upper); // JAVA
if (name.equalsIgnoreCase("JAVA")) {
System.out.println("case ignored");
}
The delta: transforming methods return new strings, and equality means content. The == operator can pass accidentally for literals, because the JVM pools them, which is exactly why this bug survives testing and explodes in production.
The String Methods You’ll Use Daily
One table covers the daily API; the complete contract lives in the String class documentation.
| Method | Example | Result |
|---|---|---|
| length() | “java”.length() | 4 |
| charAt(int) | “java”.charAt(0) | ‘j’ |
| substring(int, int) | “java”.substring(1, 3) | “av” |
| indexOf(String) | “banana”.indexOf(“na”) | 2 |
| contains(CharSequence) | “banana”.contains(“an”) | true |
| replace | “a-b”.replace(“-“, “+”) | “a+b” |
| strip() | ” x “.strip() | “x” |
| isBlank() | ” “.isBlank() | true |
| repeat(int) | “ab”.repeat(3) | “ababab” |
| split(String) | “a,b”.split(“,”) | [“a”, “b”] |
| formatted(Object…) | “Hi %s”.formatted(“Ada”) | “Hi Ada” |
Two notes complete the picture. First, String.format and its instance form formatted() beat concatenation once a message grows past a few pieces. Second, multiline literals get their own syntax, text blocks, which arrives later in the course with the other modern language features.
StringBuilder: When Concatenation Gets Heavy
The + operator is pleasant and, occasionally, expensive. Each concatenation builds a new String and copies every existing character into it, so n joins cost roughly n squared character copies. As a result, inside a loop over thousands of items, that quadratic behavior becomes a line on your dashboards.
// WRONG: quadratic in a loop
String report = "";
for (int i = 1; i <= 100_000; i++) {
report += i + ","; // copies the whole report every pass
}
// RIGHT: one growing buffer
var sb = new StringBuilder();
for (int i = 1; i <= 100_000; i++) {
sb.append(i).append(',');
}
String fastReport = sb.toString();
StringBuilder keeps one internal array, appends into it, and doubles capacity when needed. The delta is algorithmic: linear versus quadratic, which in practice means the buffer version finishes in milliseconds while the plus version runs for seconds. The full API, from insert to reverse, is in the StringBuilder documentation.
For simple joins, skip the builder entirely, because the JDK already built it for you:
String csvLine = String.join(",", "a", "b", "c"); // a,b,c
String dashes = String.join(" - ", "one", "two"); // one - two
Arrays of Strings in Java: The args Array You Already Use
Every Java program receives its command line as a String[] args in main. Parsing that array is your first real arrays-plus-strings pipeline, and it is exactly what the calculator practice will do:
public class Greet {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("usage: java Greet <name>");
return;
}
String name = args[0];
System.out.println("Hello, " + name + "!");
}
}
For example, one split turns a CSV line into an array, and one loop assembles a report from it. Small pieces, but that composition is the daily bread of backend Java.
How Real Systems Do This
Real backends split simple input with split, and then wish they had not, because split takes a regular expression. Splitting on a comma is fine; splitting real CSV is not, because quoted fields and embedded separators exist. The CSV analyzer practice article walks that boundary with a parser you write yourself, and at production scale teams reach for a dedicated CSV library for the same reason they reach for a real JSON parser instead of hand-rolled string surgery.
In fact, security teams treat strings as memory. A password in a String lives in the heap until the garbage collector clears it, and you cannot clear it on demand. Therefore, credential-handling APIs ask for char[], which you can zero out deterministically: java.util.Arrays.fill(password, ‘\0’) is a real line in real codebases.
In my experience, the two string bugs that survive review are == comparisons and loop concatenation. The first passes every unit test that uses literals; the second never appears until the data grows. Both are one-line fixes, and both cost teams I have worked with more than a day each to find.
Decision Framework
- Is the count of values fixed at creation? Use an array. Does it grow at runtime? Use a List; the collections article covers the options.
- Is the text stable once built? Use String. Assembling it in pieces or inside a loop? Use StringBuilder.
- Joining with a separator? String.join, no loop needed.
- Splitting simple, well-formed input? split works. Quoted fields, escapes, embedded newlines? Use a real parser.
- Holding a secret you must be able to erase? char[] plus Arrays.fill, not String.
When NOT to Use This
- Do not use arrays as your default container. Arrays suit fixed shapes: lookup tables, buffers, grids. Dynamic data belongs in collections, which Part 3 opens with List and Set.
- Do not build strings with + inside loops that run thousands of times or more. After all, the cost is quadratic, and it always surfaces in production first.
- Do not parse structured formats with split and hope. CSV with quotes, JSON, and log formats need real parsers; a regex split is a prototype, not a product.
Common Mistakes
- Comparing strings with ==. The literal pool makes tests pass; runtime-built strings make it fail exactly when it matters.
- Discarding the result of toUpperCase, replace, or strip. String returns a new value; it never edits in place.
- split(“.”) returns an empty array, because the dot means “any character” in regex. Escape it as “\\.”.
- Mixing up length and length(): arrays expose a field, strings expose a method.
- Comparing array contents with ==: array equality is identity. Use Arrays.equals for contents.
- Sizing a String[] “for now” and hand-copying later. That is a List, rebuilt badly.
Key Takeaways
- Arrays are fixed-length, zero-indexed, and bounds-checked at runtime; length is a field, and element defaults are 0, false, or null.
- java.util.Arrays supplies copyOf, sort, toString, and equals; contents are compared with Arrays.equals, never ==.
- String is immutable: transformations return new strings, so capture results and compare with equals.
- + concatenation is quadratic in loops; StringBuilder is linear, and String.join uses the same machinery for simple joins.
- split takes a regex: escape special characters like the dot, and treat real CSV as a parsing problem, not a split problem.
- Choose arrays for fixed shapes, List for growing data, char[] for secrets you must wipe.
- args is a String[]; command-line parsing is your first arrays-and-strings pipeline.
FAQ
Is String mutable or immutable in Java?
Immutable. Every method that appears to change a string returns a new one, and the original stays untouched. That makes strings safe to share and cheap to cache.
What is the difference between String and StringBuilder in Java?
String is an immutable value; StringBuilder is a mutable buffer for assembling one. Build with StringBuilder inside loops, then call toString to produce the finished String.
How do I compare two strings in Java?
Use equals for exact matches and equalsIgnoreCase to ignore case. The == operator compares references, and literals make it accidentally true in tests, which is why the bug spreads.
Why does split(“.”) return an empty array in Java?
Because split takes a regular expression, and a dot matches any character. Escape it as split(“\\.”) to split on literal dots.
Should I use an array or an ArrayList in Java?
Use an array when the size is fixed and known in advance; use ArrayList when the data grows at runtime. Part 3 shows why collections become the default in production code.
Conclusion
Arrays give you fixed, fast, checked storage; String gives you safe, immutable text; StringBuilder gives you the engine for building text at speed. Together they cover nearly all data handling you will write before collections arrive in Part 3.
Use arrays for shape, String for meaning, and StringBuilder for speed. When in doubt, that sentence is the whole article.
Last updated on 25 September 2026.
