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.