Programming foundations: Java

Sefism.

Chapter 02

Collections, errors and files

The core collections you use every day, what happens when you share them, and how to handle errors, files and code split across modules.

Lists and dynamic arrays

Also called array, ArrayList, vector, List<T>

What
An ordered collection that grows as you add items and gives fast access by index: list in Python, arrays in JavaScript, ArrayList in Java, std::vector in C++ and List<T> in C#.
Why
It is the default collection for "a bunch of things in order". Reach for something else only when you need fast lookup by key or uniqueness.
Java
List<Integer> nums = new ArrayList<>(List.of(3, 1, 2));
nums.add(5);
Collections.sort(nums);
int first = nums.getFirst();
int last = nums.getLast();
List<Integer> evens = nums.stream()
    .filter(n -> n % 2 == 0)
    .toList();
System.out.println(nums.size() + " " + nums.contains(2));

Watch out

Removing items from a list while looping over it skips elements; build a new list or filter instead.

Dictionaries and maps

Also called dict, HashMap, Map, unordered_map, Dictionary, key-value

What
A collection of key and value pairs where you look a value up by its key, such as a product name to its stock count. Lookups, inserts and deletes take constant time on average.
Why
Use one whenever you would otherwise search a list for a matching item: counting, grouping, caching and indexing records by id.
Java
Map<String, Integer> stock = new HashMap<>();
stock.put("apple", 3);
stock.merge("apple", 1, Integer::sum);
int count = stock.getOrDefault("kiwi", 0);
for (var e : stock.entrySet()) {
  System.out.println(e.getKey() + " " + e.getValue());
}

Watch out

Reading a missing key: Python and C# throw, Java and JavaScript give null or undefined, and C++ [] silently inserts. Use a get with a default.

Sets

Also called HashSet, unordered_set, unique values

What
An unordered collection of unique values with fast membership checks. Adding a value that is already there does nothing.
Why
Use a set to remove duplicates, to check "have I seen this before?" in constant time, or to find items two groups have in common.
Java
Set<String> seen = new HashSet<>();
for (String email : emails) {
  if (!seen.add(email)) {
    System.out.println("duplicate: " + email);
  }
}

Watch out

Checking membership with a list inside a loop, which makes the loop quadratic when a set would keep it linear.

Mutability and copying

Also called shallow copy, deep copy, aliasing, immutable

What
A mutable value can change in place; an immutable one cannot. In Python, JavaScript, Java and C#, assigning a list to a second variable shares the same list. In C++ assignment copies unless you use a reference.
Why
Two names for one list is the source of many "why did my data change?" bugs. Knowing when you copy and when you share lets you avoid them.
Java
List<Integer> a = new ArrayList<>(List.of(1, 2));
List<Integer> b = a;
b.add(3);
System.out.println(a); // [1, 2, 3]
List<Integer> c = new ArrayList<>(a);
c.add(4);
System.out.println(a); // [1, 2, 3]

Watch out

Assuming a shallow copy is independent: nested lists or objects inside it are still shared with the original.

Exceptions

Also called try catch, error handling, throw, raise

What
An exception is an error object thrown when something goes wrong. It stops normal flow and travels up the call stack until a try/catch (Python: try/except) handles it.
Why
Exceptions keep error handling out of the happy path and let the layer that knows what to do (retry, show a message, log) deal with the problem.
Java
static double parseAmount(String text) {
  try {
    return Double.parseDouble(text);
  } catch (NumberFormatException e) {
    throw new IllegalArgumentException("bad amount " + text, e);
  }
}
try {
  parseAmount("12,50");
} catch (IllegalArgumentException e) {
  System.out.println(e.getMessage());
}

Watch out

Catching every exception and carrying on silently: the program keeps running with bad data and the real cause is lost.

Reading files and cleaning up

Also called with, try-with-resources, using, RAII, finally

What
Files, network connections and locks must be closed when you are done, even if an error happens halfway. Each language has a construct that does this for you: with, try-with-resources, using, finally or a C++ destructor.
Why
Unclosed files leak handles, can lose buffered writes and lock the file on Windows. The built-in construct makes cleanup automatic instead of something you must remember.
Java
Path path = Path.of("notes.txt");
try (BufferedReader in = Files.newBufferedReader(path)) {
  String line;
  while ((line = in.readLine()) != null) {
    System.out.println(line);
  }
} catch (IOException e) {
  System.err.println("Could not read: " + e.getMessage());
}

Watch out

Opening text files without naming the encoding, so the program reads accented text correctly on one machine and garbles it on another.

Modules and imports

Also called packages, namespaces, header files, import

What
A module is a file (or group of files) of related code that other files import by name. Python and JavaScript import modules, Java uses packages, C# uses namespaces and C++ includes header files.
Why
Splitting code into modules keeps each file focused, avoids name clashes and lets you reuse and test parts on their own.
Java
// src/shop/Pricing.java
package shop;

public class Pricing {
  public static double withTax(double amount) {
    return amount * 1.2;
  }
}

// src/app/Main.java
import shop.Pricing;

Watch out

Circular imports, where two modules import each other: move the shared code into a third module.

Reading errors and debugging

Also called stack trace, traceback, breakpoint, debugger

What
A stack trace lists the chain of function calls that led to an error, with file names and line numbers. A debugger lets you pause at a breakpoint, step line by line and inspect variables.
Why
Reading the trace tells you where to look in seconds, and stepping through code shows what it actually does instead of what you assume it does.
Example
A trace ending in KeyError: 'total' at report.py line 42, called from main.py line 10, means line 42 reads a key the dictionary does not have. Set a breakpoint there and inspect the dictionary.

Watch out

Reading only the last line of a long error, or fixing the last of many compiler errors, when the first one usually causes the rest.

02 · Collections, errors and files2 / 10