Recent Posts
Archives

Posts Tagged ‘JavaEvolution’

PostHeaderIcon [VoxxedDaysAmsterdam2026] Stream Tricks That You Don’t Wanna Miss: Enhancing Java Streams with Gatherers and String Templates in JDK 25

Lecturer
Aicha Laafia is a Java software engineer at Havana Group, currently based in France while originally from Morocco. She is passionate about sustainable technology, green programming, and advocating for greater representation of women in tech. Aicha actively participates in various communities, serves as a Women Techmakers and Girls Code ambassador, and facilitates IAmRemarkable workshops. She was recently promoted to Oracle ACE Associate, recognizing her contributions to the Java ecosystem.

Abstract
In this engaging session from Voxxed Days Amsterdam 2026, Aicha Laafia explores significant enhancements to Java’s stream processing capabilities and string handling introduced in JDK 25. She addresses longstanding pain points with traditional streams—such as the inability to maintain state mid-pipeline, complex custom collectors for batching or sliding windows, and error-prone string concatenation for SQL, JSON, or logs—through the new Stream Gatherers API and String Templates. Drawing on live code demonstrations and relatable examples from Formula 1 racing data, the presentation illustrates how these features introduce memory and statefulness to streams, simplify data transformations, and promote safer, more readable code. The talk underscores Java’s continued evolution toward more expressive and maintainable programming paradigms, encouraging developers to upgrade and share knowledge about these advancements.

The Persistent Challenges with Traditional Java Streams

Java developers have long appreciated streams for producing cleaner, more declarative, and expressive code compared to imperative loops. However, as Aicha points out, streams can occasionally leave programmers feeling frustrated or even “like complete idiots” when attempting advanced operations. The core limitation stems from the stateless nature of intermediate operations like map, filter, or flatMap. Each element processes independently and is immediately forgotten, making it impossible to track accumulated state, create overlapping windows, or group data mid-pipeline without terminating the stream via a collector.

Common pain points include manual batching implementations that rely on counters, lists, and careful index management to avoid off-by-one errors or lost elements. Grouping overlapping data—for instance, creating sliding windows of size n for rolling averages or trend detection—often requires intricate custom collectors that become difficult to understand or maintain over time, even for the original author. Furthermore, once a collector is applied, the pipeline ends; no further stream operations are possible afterward. These issues lead to verbose, error-prone code or a reluctant fallback to traditional for-loops, undermining the very benefits streams were meant to deliver.

Aicha emphasizes that these problems arise because prior to JDK 25, streams lacked “memory.” Elements flowed through independently without retaining context from previous items, forcing developers into workarounds that compromised readability and maintainability.

Introducing Stream Gatherers: Bringing Memory and Flexibility to Streams

JDK 25 addresses these limitations head-on with the Stream Gatherers API, which equips streams with stateful processing capabilities while remaining intermediate operations. Unlike terminal collectors, gatherers allow continued chaining after stateful transformations. A gatherer consists of up to four components, though only the integrator is mandatory:

  • Initializer (optional): Executes once before any elements arrive, establishing initial state such as an empty list or counter.
  • Integrator: The core logic, invoked for every element. It receives the current element, the mutable state, and a downstream consumer. Developers implement accumulation or transformation here, returning true to continue or false to short-circuit the pipeline.
  • Combiner (optional): Essential for parallel streams, merging partial states from different threads.
  • Finisher (optional): Runs once at the end of the stream, ensuring no residual state (such as an incomplete final batch) is lost by pushing any remaining elements downstream.

This design provides a short-circuit mechanism and supports parallel execution when a combiner is supplied. Aicha demonstrates creating a custom batching gatherer in roughly 15 lines of code—far simpler than equivalent custom collectors or manual loops. The initializer creates an empty list; the integrator adds elements until the batch size is reached, then pushes the batch downstream and clears the buffer; the finisher handles any trailing incomplete batch.

Even better, JDK 25 ships with five built-in gatherers that eliminate most custom implementations:

  • windowFixed(n): Produces non-overlapping batches of exactly size n, including a final potentially smaller batch.
  • windowSliding(n): Generates overlapping windows, ideal for rolling calculations, trend detection, or analyzing sequential data patterns in production monitoring.
  • scan: Accumulates intermediate results similar to a fold, emitting every partial value starting from an initial element—unlike reduce, which yields only the final result.
  • fold: Similar accumulation but treats the operation as intermediate, returning an Optional while permitting further pipeline chaining.
  • mapConcurrent(maxConcurrency, mapper): Executes the mapper on virtual threads (up to the specified concurrency limit) while preserving encounter order, making it particularly suited for I/O-bound tasks without manual thread management.

These tools transform previously cumbersome tasks into concise, readable one- or few-line operations.

Live Demonstration: Analyzing Formula 1 Data with Gatherers

To illustrate practical application, Aicha uses racing data from Max Verstappen’s 2025 Formula 1 season, modeled as a record containing round number, Grand Prix name, position, and points. She contrasts traditional approaches—often involving dozens of lines of custom collector code with initializer, accumulator, combiner, and finisher—with gatherer-based solutions.

For batching every three races to compute cumulative points and wins, a windowFixed(3) gatherer replaces extensive custom logic, producing clean batches while automatically handling the final incomplete group. Sliding windows demonstrate overlapping views, such as performance trends across consecutive race triplets, again in just a few lines.

Accumulation across the entire season uses scan to emit running totals after each race, revealing Verstappen’s final 421 points and near-miss championship outcome. These examples highlight how gatherers retain “memory” of prior elements, enabling stateful yet fluent pipelines.

Aicha also touches on String Templates, another JDK 25 feature that enhances safety and readability. Traditional string concatenation or String.format often leads to injection vulnerabilities in SQL or JSON and creates “plus soup” that is hard to read. String Templates provide a clean, type-safe interpolation mechanism that reduces errors and improves security for logging, queries, and data serialization.

Implications and Recommendations for Modern Java Development

The introduction of gatherers and string templates reflects Java’s ongoing commitment to evolving without breaking compatibility, offering developers more powerful abstractions while preserving the language’s robustness. By reducing reliance on custom collectors and imperative workarounds, these features promote more maintainable, expressive codebases that are easier to reason about and debug.

Gatherers particularly shine in data processing pipelines, analytics, monitoring, and any domain requiring windowed or accumulated views. Their support for parallelism and short-circuiting adds efficiency, while the built-in variants cover the majority of common use cases, lowering the barrier to advanced stream usage.

Aicha encourages the community to upgrade to the latest JDK, experiment with these capabilities, write articles, and deliver talks to spread awareness. She notes that many scenarios previously abandoned to “for-loop hell” now become elegant stream solutions thanks to gatherers.

Code Sample: Batching with windowFixed

// Traditional complex collector approach omitted for brevity

// With Gatherers in JDK 25
var batches = races.stream()
    .gather(Gatherers.windowFixed(3))
    .map(batch -> computeStats(batch))  // e.g., sum points, count wins
    .toList();

Code Sample: Sliding Window for Trends

var slidingWindows = races.stream()
    .gather(Gatherers.windowSliding(3))
    .map(window -> analyzeTrend(window))
    .toList();

Code Sample: Accumulation with scan

var runningTotals = pointsStream
    .gather(Gatherers.scan(() -> 0, Integer::sum))
    .toList();  // Emits every intermediate sum

These snippets demonstrate the dramatic reduction in complexity while preserving full pipeline fluency.

In conclusion, Aicha Laafia’s presentation provides both a clear diagnosis of historical stream limitations and a compelling vision for their resolution in JDK 25. By incorporating statefulness through gatherers and safer string handling, Java strengthens its position as a modern, versatile language suitable for complex data-driven applications. Developers who adopt these features will benefit from shorter, more readable code, fewer maintenance headaches, and enhanced productivity.

Links:

PostHeaderIcon [DevoxxFR2012] Cloud Foundry manifest (manifest.yml)

applications:
– name: sample-java-app
memory: 512M
instances: 2
path: target/sample-java-app.war
services:
mysql-service:
type: mysql


## Reimagining Software Craftsmanship in the Cloud Era
The cloud era reshapes not only infrastructure but the software development lifecycle. Patrick likens modern software to the fashion industry: iPhone apps follow seasonal cycles—Angry Birds Space, Angry Birds Seasons—demanding rapid iteration and monetization within shrinking windows. A/B testing, a data-driven methodology, becomes essential for optimizing user engagement. In enterprises, “situational applications” proliferate—short-lived tools like the Devoxx website or a two-week Cloud Foundry tour prototype—contrasting with decade-long monoliths.

Kent Beck’s “Software G-forces” framework, presented a year prior, adapts agile practices to deployment cadence. Annual releases tolerate heavyweight processes; hourly deployments demand extreme lightness. Cloud’s primary business value, Patrick asserts, lies in liberating developers from infrastructure toil, enabling focus on domain logic and user value. He references Greg Vanback’s domain modeling talk, advocating domain-specific languages (DSLs) to encode business rules over plumbing.

Lock-in remains the cloud’s Achilles’ heel, evocatively termed the “Hotel California syndrome” by VMware CEO Paul Maritz: entry is easy, exit impossible. Cloud Foundry counters this through open-source neutrality, allowing code to run identically on-premises or across providers. Patrick’s transition from Google to VMware was motivated by this philosophy—empowering developers to own their destiny.

// Spring Boot on Cloud Foundry (conceptual)
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
“`

Forecasting the Developer’s Future: Lessons and Imperatives

Patrick predicts software will increasingly resemble fashion, prioritizing design excellence and tool versatility. Java developers must transcend the “hammer complex”—viewing every problem as a nail for their familiar tool—and embrace polyglot programming to unlock novel solutions. Obsolete concepts like First Normal Form or Waterfall methodologies must be unlearned; agile practices, API design, A/B testing, and framework diversity must be mastered.

The fictional George’s redemption arc offers a blueprint. After months of unemployment in 2010, a Paris JUG meetup rekindles his passion. Surrounded by peers wielding Scala, Node.js, HTML5, and agile since 2007, he invests in an iPad, iPhone, and MacBook Pro. Joining the Cantine coworking space, he codes daily with unit tests, devours Reid Hoffman’s The Start-Up of You and Gerald Weinberg’s The Psychology of Computer Programming, and treats his career as a startup. Contributing to open-source, he pushes code via Git, Jenkins, and VMC. His mobile app scales to 10 million users on cloud infrastructure he never manages, eventually acquired (perhaps by Viadeo in France). Abandoning golf for samba in Brazil, George embodies reinvention.

Conclusion: Authoring the Developer’s Comedy

Technological revolutions, like cinema’s sound era, compel adaptation or obsolescence. Developers must shed complexity worship, embrace platform abstraction, and center users through agile, data-driven practices. Open-source PaaS like Cloud Foundry democratizes innovation, mitigating lock-in and accelerating community contributions. Patrick’s narrative—part memoir, part manifesto—urges developers to engage communities, master emerging paradigms, and view their careers as entrepreneurial ventures. In this American comedy, the developer’s story ends triumphantly, provided they seize authorship of their destiny.

Links: