Skip to content

Commit 96d2a22

Browse files
brunoborgesCopilot
andcommitted
Add 4 new patterns: String.lines, Predicate.not, orElseThrow, RandomGenerator
New snippets filling coverage gaps between JDK 8 and 25: - strings/string-lines (JDK 11): String.lines() for line splitting - streams/predicate-not (JDK 11): Predicate.not() for negation - errors/optional-orelsethrow (JDK 10): Optional.orElseThrow() no-arg - security/random-generator (JDK 17): RandomGenerator interface Total snippets: 85 → 89 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a41f5f6 commit 96d2a22

File tree

9 files changed

+241
-4
lines changed

9 files changed

+241
-4
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"id": 88,
3+
"slug": "optional-orelsethrow",
4+
"title": "Optional.orElseThrow() without supplier",
5+
"category": "errors",
6+
"difficulty": "beginner",
7+
"jdkVersion": "10",
8+
"oldLabel": "Java 8",
9+
"modernLabel": "Java 10+",
10+
"oldApproach": "get() or orElseThrow(supplier)",
11+
"modernApproach": "orElseThrow()",
12+
"oldCode": "// Risky: get() throws if empty, no clear intent\nString value = optional.get();\n\n// Verbose: supplier just for NoSuchElementException\nString value = optional\n .orElseThrow(NoSuchElementException::new);",
13+
"modernCode": "// Clear intent: throws NoSuchElementException if empty\nString value = optional.orElseThrow();",
14+
"summary": "Use Optional.orElseThrow() as a clearer, intent-revealing alternative to get().",
15+
"explanation": "Optional.get() is widely considered a code smell because it hides the possibility of failure. The no-arg orElseThrow(), added in Java 10, does exactly the same thing but makes the intent explicit: the developer expects a value and wants an exception if absent.",
16+
"whyModernWins": [
17+
{ "icon": "📖", "title": "Self-documenting", "desc": "orElseThrow() clearly signals that absence is unexpected." },
18+
{ "icon": "🔒", "title": "Avoids get()", "desc": "Static analysis tools flag get() as risky; orElseThrow() is idiomatic." },
19+
{ "icon": "", "title": "Less boilerplate", "desc": "No need to pass a supplier for the default NoSuchElementException." }
20+
],
21+
"support": {
22+
"state": "available",
23+
"description": "Available since JDK 10 (March 2018)."
24+
},
25+
"prev": "errors/record-based-errors",
26+
"next": "datetime/java-time-basics",
27+
"related": [
28+
"errors/optional-chaining",
29+
"streams/optional-or",
30+
"errors/require-nonnull-else"
31+
]
32+
}

content/errors/record-based-errors.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"description": "Widely available since JDK 16 (March 2021)"
3636
},
3737
"prev": "errors/null-in-switch",
38-
"next": "datetime/java-time-basics",
38+
"next": "errors/optional-orelsethrow",
3939
"related": [
4040
"errors/helpful-npe",
4141
"errors/require-nonnull-else",
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"id": 89,
3+
"slug": "random-generator",
4+
"title": "RandomGenerator interface",
5+
"category": "security",
6+
"difficulty": "intermediate",
7+
"jdkVersion": "17",
8+
"oldLabel": "Java 8",
9+
"modernLabel": "Java 17+",
10+
"oldApproach": "new Random() / ThreadLocalRandom",
11+
"modernApproach": "RandomGenerator factory",
12+
"oldCode": "// Hard-coded to one algorithm\nRandom rng = new Random();\nint value = rng.nextInt(100);\n\n// Or thread-local, but still locked in\nint value = ThreadLocalRandom.current()\n .nextInt(100);",
13+
"modernCode": "// Algorithm-agnostic via factory\nvar rng = RandomGenerator.of(\"L64X128MixRandom\");\nint value = rng.nextInt(100);\n\n// Or get a splittable generator\nvar rng = RandomGeneratorFactory\n .of(\"L64X128MixRandom\").create();",
14+
"summary": "Use the RandomGenerator interface to choose random number algorithms by name without coupling to a specific class.",
15+
"explanation": "JDK 17 introduced RandomGenerator as a common interface for all RNG implementations. Instead of hard-coding new Random() or ThreadLocalRandom, you can select algorithms by name via a factory, making it easy to swap between algorithms optimized for different use cases (speed, statistical quality, splittability).",
16+
"whyModernWins": [
17+
{ "icon": "🔧", "title": "Algorithm-agnostic", "desc": "Choose the best RNG algorithm by name without changing code structure." },
18+
{ "icon": "", "title": "Better algorithms", "desc": "Access to modern LXM generators with superior statistical properties." },
19+
{ "icon": "🔗", "title": "Unified API", "desc": "One interface covers Random, ThreadLocalRandom, SplittableRandom, and more." }
20+
],
21+
"support": {
22+
"state": "available",
23+
"description": "Available since JDK 17 (September 2021, JEP 356)."
24+
},
25+
"prev": "security/tls-default",
26+
"next": "tooling/jshell-prototyping",
27+
"related": [
28+
"security/strong-random",
29+
"security/key-derivation-functions",
30+
"concurrency/virtual-threads"
31+
]
32+
}

content/security/tls-default.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"description": "Widely available since JDK 11 (Sept 2018)"
3636
},
3737
"prev": "security/strong-random",
38-
"next": "tooling/jshell-prototyping",
38+
"next": "security/random-generator",
3939
"related": [
4040
"security/pem-encoding",
4141
"security/key-derivation-functions",

content/streams/optional-or.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"description": "Widely available since JDK 9 (Sept 2017)"
3636
},
3737
"prev": "streams/optional-ifpresentorelse",
38-
"next": "concurrency/virtual-threads",
38+
"next": "streams/predicate-not",
3939
"related": [
4040
"streams/stream-iterate-predicate",
4141
"streams/stream-gatherers",

content/streams/predicate-not.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"id": 87,
3+
"slug": "predicate-not",
4+
"title": "Predicate.not() for negation",
5+
"category": "streams",
6+
"difficulty": "beginner",
7+
"jdkVersion": "11",
8+
"oldLabel": "Java 8",
9+
"modernLabel": "Java 11+",
10+
"oldApproach": "Lambda negation",
11+
"modernApproach": "Predicate.not()",
12+
"oldCode": "List<String> nonEmpty = list.stream()\n .filter(s -> !s.isBlank())\n .collect(Collectors.toList());",
13+
"modernCode": "List<String> nonEmpty = list.stream()\n .filter(Predicate.not(String::isBlank))\n .toList();",
14+
"summary": "Use Predicate.not() to negate method references cleanly instead of writing lambda wrappers.",
15+
"explanation": "Before Java 11, negating a method reference required wrapping it in a lambda. Predicate.not() lets you negate any predicate directly, keeping the code readable and consistent with method reference style throughout the stream pipeline.",
16+
"whyModernWins": [
17+
{ "icon": "👁", "title": "Cleaner negation", "desc": "No need to wrap method references in lambdas just to negate them." },
18+
{ "icon": "🔗", "title": "Composable", "desc": "Works with any Predicate, enabling clean predicate chains." },
19+
{ "icon": "📖", "title": "Reads naturally", "desc": "Predicate.not(String::isBlank) reads like English." }
20+
],
21+
"support": {
22+
"state": "available",
23+
"description": "Available since JDK 11 (September 2018)."
24+
},
25+
"prev": "streams/optional-or",
26+
"next": "concurrency/virtual-threads",
27+
"related": [
28+
"streams/stream-tolist",
29+
"strings/string-isblank",
30+
"streams/stream-takewhile-dropwhile"
31+
]
32+
}

content/strings/string-chars-stream.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"description": "Available since JDK 8+ (improved in 9+)"
3636
},
3737
"prev": "strings/multiline-json-sql",
38-
"next": "streams/stream-of-nullable",
38+
"next": "strings/string-lines",
3939
"related": [
4040
"strings/string-repeat",
4141
"strings/string-isblank",

content/strings/string-lines.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"id": 86,
3+
"slug": "string-lines",
4+
"title": "String.lines() for line splitting",
5+
"category": "strings",
6+
"difficulty": "beginner",
7+
"jdkVersion": "11",
8+
"oldLabel": "Java 8",
9+
"modernLabel": "Java 11+",
10+
"oldApproach": "split(\"\\\\n\")",
11+
"modernApproach": "lines()",
12+
"oldCode": "String text = \"one\\ntwo\\nthree\";\nString[] lines = text.split(\"\\n\");\nfor (String line : lines) {\n System.out.println(line);\n}",
13+
"modernCode": "String text = \"one\\ntwo\\nthree\";\ntext.lines().forEach(System.out::println);",
14+
"summary": "Use String.lines() to split text into a stream of lines without regex overhead.",
15+
"explanation": "String.lines() returns a Stream<String> of lines split by \\n, \\r, or \\r\\n. It is lazier and more efficient than split(), avoids regex compilation, and integrates naturally with the Stream API for further processing.",
16+
"whyModernWins": [
17+
{ "icon": "", "title": "Lazy streaming", "desc": "Lines are produced on demand, not all at once like split()." },
18+
{ "icon": "🔧", "title": "Universal line endings", "desc": "Handles \\n, \\r, and \\r\\n automatically without regex." },
19+
{ "icon": "🔗", "title": "Stream integration", "desc": "Returns a Stream for direct use with filter, map, collect." }
20+
],
21+
"support": {
22+
"state": "available",
23+
"description": "Available since JDK 11 (September 2018)."
24+
},
25+
"prev": "strings/string-chars-stream",
26+
"next": "streams/stream-of-nullable",
27+
"related": [
28+
"strings/string-isblank",
29+
"strings/string-strip",
30+
"strings/string-indent-transform"
31+
]
32+
}

site/index.html

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,33 @@ <h3>String chars as stream</h3>
13091309
<span class="arrow-link"></span>
13101310
</div>
13111311
</a>
1312+
<a href="/strings/string-lines.html" class="tip-card" data-category="strings">
1313+
<div class="tip-card-body">
1314+
<div class="tip-card-header">
1315+
<div class="tip-badges"><span class="badge strings">Strings</span></div>
1316+
</div>
1317+
<h3>String.lines() for line splitting</h3>
1318+
</div>
1319+
<div class="card-code">
1320+
<div class="card-code-layer old-layer">
1321+
<span class="mini-label">Old</span>
1322+
<span class="code-text">String[] lines = text.split(&quot;\n&quot;);
1323+
for (String line : lines) {
1324+
System.out.println(line);
1325+
}</span>
1326+
</div>
1327+
<div class="card-code-layer modern-layer">
1328+
<span class="mini-label">Modern</span>
1329+
<span class="code-text">text.lines()
1330+
.forEach(System.out::println);</span>
1331+
</div>
1332+
<span class="hover-hint">hover to see modern →</span>
1333+
</div>
1334+
<div class="tip-card-footer">
1335+
<span class="browser-support"><span class="dot"></span> JDK 11+</span>
1336+
<span class="arrow-link"></span>
1337+
</div>
1338+
</a>
13121339
<a href="/streams/stream-of-nullable.html" class="tip-card" data-category="streams">
13131340
<div class="tip-card-body">
13141341
<div class="tip-card-header">
@@ -1621,6 +1648,33 @@ <h3>Optional.or() fallback</h3>
16211648
<span class="arrow-link"></span>
16221649
</div>
16231650
</a>
1651+
<a href="/streams/predicate-not.html" class="tip-card" data-category="streams">
1652+
<div class="tip-card-body">
1653+
<div class="tip-card-header">
1654+
<div class="tip-badges"><span class="badge streams">Streams</span></div>
1655+
</div>
1656+
<h3>Predicate.not() for negation</h3>
1657+
</div>
1658+
<div class="card-code">
1659+
<div class="card-code-layer old-layer">
1660+
<span class="mini-label">Old</span>
1661+
<span class="code-text">list.stream()
1662+
.filter(s -&gt; !s.isBlank())
1663+
.collect(Collectors.toList());</span>
1664+
</div>
1665+
<div class="card-code-layer modern-layer">
1666+
<span class="mini-label">Modern</span>
1667+
<span class="code-text">list.stream()
1668+
.filter(Predicate.not(String::isBlank))
1669+
.toList();</span>
1670+
</div>
1671+
<span class="hover-hint">hover to see modern →</span>
1672+
</div>
1673+
<div class="tip-card-footer">
1674+
<span class="browser-support"><span class="dot"></span> JDK 11+</span>
1675+
<span class="arrow-link"></span>
1676+
</div>
1677+
</a>
16241678
<a href="/concurrency/virtual-threads.html" class="tip-card" data-category="concurrency">
16251679
<div class="tip-card-body">
16261680
<div class="tip-card-header">
@@ -2411,6 +2465,35 @@ <h3>Record-based error responses</h3>
24112465
<span class="arrow-link"></span>
24122466
</div>
24132467
</a>
2468+
<a href="/errors/optional-orelsethrow.html" class="tip-card" data-category="errors">
2469+
<div class="tip-card-body">
2470+
<div class="tip-card-header">
2471+
<div class="tip-badges"><span class="badge errors">Errors</span></div>
2472+
</div>
2473+
<h3>Optional.orElseThrow() without supplier</h3>
2474+
</div>
2475+
<div class="card-code">
2476+
<div class="card-code-layer old-layer">
2477+
<span class="mini-label">Old</span>
2478+
<span class="code-text">// Risky: get() throws if empty
2479+
String value = optional.get();
2480+
2481+
// Verbose supplier
2482+
String value = optional
2483+
.orElseThrow(NoSuchElementException::new);</span>
2484+
</div>
2485+
<div class="card-code-layer modern-layer">
2486+
<span class="mini-label">Modern</span>
2487+
<span class="code-text">// Clear intent
2488+
String value = optional.orElseThrow();</span>
2489+
</div>
2490+
<span class="hover-hint">hover to see modern →</span>
2491+
</div>
2492+
<div class="tip-card-footer">
2493+
<span class="browser-support"><span class="dot"></span> JDK 10+</span>
2494+
<span class="arrow-link"></span>
2495+
</div>
2496+
</a>
24142497
<a href="/datetime/java-time-basics.html" class="tip-card" data-category="datetime">
24152498
<div class="tip-card-body">
24162499
<div class="tip-card-header">
@@ -2731,6 +2814,32 @@ <h3>TLS 1.3 by default</h3>
27312814
<span class="arrow-link"></span>
27322815
</div>
27332816
</a>
2817+
<a href="/security/random-generator.html" class="tip-card" data-category="security">
2818+
<div class="tip-card-body">
2819+
<div class="tip-card-header">
2820+
<div class="tip-badges"><span class="badge security">Security</span></div>
2821+
</div>
2822+
<h3>RandomGenerator interface</h3>
2823+
</div>
2824+
<div class="card-code">
2825+
<div class="card-code-layer old-layer">
2826+
<span class="mini-label">Old</span>
2827+
<span class="code-text">Random rng = new Random();
2828+
int value = rng.nextInt(100);</span>
2829+
</div>
2830+
<div class="card-code-layer modern-layer">
2831+
<span class="mini-label">Modern</span>
2832+
<span class="code-text">var rng = RandomGenerator
2833+
.of(&quot;L64X128MixRandom&quot;);
2834+
int value = rng.nextInt(100);</span>
2835+
</div>
2836+
<span class="hover-hint">hover to see modern →</span>
2837+
</div>
2838+
<div class="tip-card-footer">
2839+
<span class="browser-support"><span class="dot"></span> JDK 17+</span>
2840+
<span class="arrow-link"></span>
2841+
</div>
2842+
</a>
27342843
<a href="/tooling/jshell-prototyping.html" class="tip-card" data-category="tooling">
27352844
<div class="tip-card-body">
27362845
<div class="tip-card-header">

0 commit comments

Comments
 (0)