Safeguard
DevSecOps

Java Cheatsheet: Lists, Sums, Random Strings, and Everyday Idioms

A working cheatsheet for Java: modern list creation, summing collections with streams, generating random strings safely, string handling, and the idioms worth memorizing in 2025.

Safeguard Team
Product
6 min read

This cheatsheet for Java collects the everyday idioms you reach for constantly — creating and summing lists, generating random strings, string handling, and modern syntax — written the way current JDKs want them, with the gotchas called out. The habits below assume Java 17 or newer, since that is the floor most frameworks (Spring Boot 3 among them) now enforce. Everything is copy-paste runnable; the notes explain where the older way you may have memorized will bite.

Lists and maps: creation and iteration

// Immutable — throws UnsupportedOperationException on add/remove
List<String> langs = List.of("java", "kotlin", "scala");
Map<String, Integer> ports = Map.of("http", 80, "https", 443);

// Mutable when you actually need to modify
List<String> queue = new ArrayList<>(List.of("a", "b"));

// Iteration
for (String s : langs) { ... }
langs.forEach(s -> log.info(s));

// Index-free removal while iterating: use removeIf, not a for loop
queue.removeIf(s -> s.isBlank());

Gotchas worth the ink: List.of rejects null elements outright, and Arrays.asList returns a fixed-size view backed by the array — add throws, but set silently writes through to the original array. If a method hands you a list you did not build, treat it as unmodifiable until proven otherwise.

Sum of a list in Java

The canonical question. For a sum of list in Java, streams are the idiomatic answer, and the primitive-stream variant avoids boxing overhead:

List<Integer> nums = List.of(3, 1, 4, 1, 5, 9);

int sum = nums.stream().mapToInt(Integer::intValue).sum();       // 23

// Doubles
double total = prices.stream().mapToDouble(Price::amount).sum();

// Summing a field across objects
long bytes = files.stream().mapToLong(FileInfo::size).sum();

// When you need count/min/max/avg too, one pass:
IntSummaryStatistics stats = nums.stream()
        .mapToInt(Integer::intValue).summaryStatistics();

Two traps. First, nums.stream().reduce(0, Integer::sum) works but boxes every element; mapToInt is both faster and clearer. Second — and this one produces real financial bugs — never sum money as double. 0.1 + 0.2 is not 0.3 in binary floating point. Use BigDecimal with an explicit scale, or store cents in a long.

BigDecimal total = invoices.stream()
        .map(Invoice::amount)
        .reduce(BigDecimal.ZERO, BigDecimal::add);

Java: generate a random string (the safe way)

Half the snippets online for how to make Java generate a random string use java.util.Random or Math.random. Fine for test fixtures; wrong for anything security-adjacent, because Random is a seedable, predictable PRNG — tokens generated from it can be reconstructed by an observer. Session IDs, password-reset tokens, API keys, invite codes: all of these need SecureRandom.

import java.security.SecureRandom;
import java.util.Base64;

// Token: 32 random bytes, URL-safe text
SecureRandom rng = new SecureRandom();
byte[] buf = new byte[32];
rng.nextBytes(buf);
String token = Base64.getUrlEncoder().withoutPadding().encodeToString(buf);

// Human-typable code from a fixed alphabet
String alphabet = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; // no 0/O/1/I/L
StringBuilder sb = new StringBuilder(8);
for (int i = 0; i < 8; i++) {
    sb.append(alphabet.charAt(rng.nextInt(alphabet.length())));
}

Rules of thumb: 32 bytes (256 bits) for bearer tokens, at least 16 for anything an attacker could brute-force offline, and reuse one SecureRandom instance rather than constructing one per call. Predictable-randomness findings are a staple of both static analyzers and DAST scans for a reason — they keep appearing in production code because the insecure version looks identical in a demo.

Strings: the operations you actually use

String s = "  Hello, World  ";

s.strip();                       // trims Unicode whitespace (prefer over trim)
s.isBlank();                     // true for empty or whitespace-only
"ha".repeat(3);                  // "hahaha"
String.join("/", "api", "v1");   // "api/v1"
"a,b,,c".split(",", -1);         // keeps trailing empties: [a, b, , c]

// Multiline without escape soup (Java 15+)
String json = """
        {"name": "%s", "retries": %d}
        """.formatted(name, retries);

// Comparison — always equals, never ==
if (Objects.equals(a, b)) { ... }   // null-safe

The split limit argument surprises people: with no limit, trailing empty strings are dropped, which corrupts positional parsing of CSV-ish data. And == on strings compares references; it passes unit tests thanks to interning, then fails on runtime-built strings.

Modern syntax that shortens every file

// Records: immutable data carriers, one line
record Finding(String cve, String pkg, double cvss) {}

// Switch expressions with exhaustiveness checking
String band = switch ((int) cvss) {
    case 9, 10 -> "critical";
    case 7, 8  -> "high";
    case 4, 5, 6 -> "medium";
    default -> "low";
};

// Pattern matching for instanceof — no cast line
if (obj instanceof Finding f && f.cvss() >= 9.0) {
    page(f.cve());
}

// var for obvious local types
var findings = new ArrayList<Finding>();

Records deserve special mention in any java cheatsheet: they give you constructor, accessors, equals, hashCode, and toString with correct semantics, which eliminates the boilerplate where hand-written equals bugs traditionally hide.

Collections into other shapes

// Group findings by package
Map<String, List<Finding>> byPkg = findings.stream()
        .collect(Collectors.groupingBy(Finding::pkg));

// Counting per key
Map<String, Long> counts = findings.stream()
        .collect(Collectors.groupingBy(Finding::pkg, Collectors.counting()));

// Highest-severity first
List<Finding> ranked = findings.stream()
        .sorted(Comparator.comparingDouble(Finding::cvss).reversed())
        .toList();   // Java 16+; returns unmodifiable

// Distinct by a key (no built-in — idiom via toMap)
Map<String, Finding> latestByCve = findings.stream()
        .collect(Collectors.toMap(Finding::cve, f -> f, (a, b) -> b));

Note that Stream.toList() returns an unmodifiable list, unlike Collectors.toList() which historically returned a mutable ArrayList — code that appends to the result breaks when someone modernizes the collector.

Try-with-resources and time

// Resources close themselves, in reverse order, even on exception
try (var in = Files.newInputStream(path);
     var reader = new BufferedReader(new InputStreamReader(in, UTF_8))) {
    ...
}

// java.time, never java.util.Date
Instant now = Instant.now();
LocalDate today = LocalDate.now(ZoneId.of("UTC"));
Duration elapsed = Duration.between(start, Instant.now());

One closing thought that belongs in every cheatsheet java developers share internally: idioms keep your code correct, but most of a modern Java application's executable code arrives through Maven or Gradle, not your editor. Keeping those dependencies patched is the same kind of hygiene as using SecureRandom over Random — mechanical, unglamorous, and the thing audits actually check. An SCA scanner automates that half; this page covers the half you type. For deeper, structured Java security material, the Safeguard Academy has full courses.

FAQ

What is the fastest way to sum a list of integers in Java?

list.stream().mapToInt(Integer::intValue).sum(). The mapToInt step switches to a primitive IntStream, avoiding per-element boxing that reduce(0, Integer::sum) pays. For sums plus min/max/average in one pass, use summaryStatistics().

How do I generate a random string in Java for a token?

Fill a byte array from SecureRandom and Base64-url encode it: 32 bytes gives a 256-bit token. Never use java.util.Random or Math.random for tokens — their output is predictable from observed values.

Why does List.of throw when I call add?

List.of creates an immutable list; mutation methods throw UnsupportedOperationException. Wrap it when you need mutability: new ArrayList<>(List.of(...)).

Should I still use java.util.Date?

No. java.time (Instant, LocalDate, ZonedDateTime, Duration) has been the correct API since Java 8 — immutable, timezone-explicit, and thread-safe, none of which is true of Date and Calendar.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.