<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dash One</title>
    <description>The latest articles on DEV Community by Dash One (@fluentfuture).</description>
    <link>https://dev.to/fluentfuture</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1432763%2F22034d2b-6063-429b-9c28-28d809bfe02c.jpeg</url>
      <title>DEV Community: Dash One</title>
      <link>https://dev.to/fluentfuture</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fluentfuture"/>
    <language>en</language>
    <item>
      <title>Introducing SafeSql - Enforced Injection Safety</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Thu, 17 Jul 2025 01:33:07 +0000</pubDate>
      <link>https://dev.to/fluentfuture/introducing-safesql-3khk</link>
      <guid>https://dev.to/fluentfuture/introducing-safesql-3khk</guid>
      <description>&lt;h2&gt;
  
  
  SQL Injection
&lt;/h2&gt;

&lt;p&gt;In Java, JDBC &lt;code&gt;PreparedStatement&lt;/code&gt; is the vital defense against SQL Injection (SQLi).&lt;/p&gt;

&lt;p&gt;However when developers need to build &lt;strong&gt;dynamic SQL&lt;/strong&gt; (imagine if you need to parameterize by table name, column names, or have complex subqueries to reuse), &lt;code&gt;PreparedStatement&lt;/code&gt;'s parameterization support becomes insufficient and they usually have to fall back to raw string concatenation, opening the door to injection risks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NotPetya (2017) – Up to $10 Billion:&lt;/strong&gt; This ransomware attack, which exploited initial vulnerabilities that could include web application flaws, caused unprecedented global disruption and staggering financial losses for giants like Maersk and FedEx.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Equifax (2017) – Over $1.4 Billion:&lt;/strong&gt; A failure to patch a known web application vulnerability exposed sensitive data for 147 million people, leading to massive settlements, fines, and enduring reputational damage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When large, complex systems rely on &lt;em&gt;programmer caution and code reviews&lt;/em&gt; for dynamic SQL string concatenation, the risk is a timed bomb. Human errors, vast codebase, developer turnover, and rushed reviews make it impossible to manually prevent every subtle SQLi vulnerability.&lt;/p&gt;

&lt;p&gt;Humans make mistake, they always do.&lt;/p&gt;




&lt;h2&gt;
  
  
  Existing Mitigations
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Hardening the SQL String
&lt;/h4&gt;

&lt;p&gt;Google internally uses the &lt;code&gt;TrustedSqlString&lt;/code&gt; class. By taking advantage of ErrorProne's compile-time check, it ensures that only trusted sql snippets (compile-time string constants, flag values etc.) are allowed to be concatenated.&lt;/p&gt;

&lt;p&gt;The Spanner library builds on top of &lt;code&gt;TrustedSqlString&lt;/code&gt;, by supporting &lt;code&gt;bind("id", id)&lt;/code&gt; calls for named parameters like &lt;code&gt;@id&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;It works, but when you have complex subqueries to be composed together, each with their own parameters, you'll have to manage and merge the parameters yourself, making sure the parameter names don't clash etc. &lt;/p&gt;

&lt;p&gt;This can still result in fragmented SQL and complex parameter managing code.&lt;/p&gt;

&lt;h4&gt;
  
  
  SQL Template With Escape Hatch
&lt;/h4&gt;

&lt;p&gt;MyBatis offers the &lt;code&gt;#{}&lt;/code&gt; syntax for filling in template parameters and passing them as &lt;code&gt;PreparedStatement&lt;/code&gt; parameters.&lt;/p&gt;

&lt;p&gt;For dynamic SQL (table names etc.),  the &lt;code&gt;${}&lt;/code&gt; syntax is used to allow developers to fill in arbitrary string, an escape hatch and a "be careful, you are on your own" warning.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;${}&lt;/code&gt; escape hatch is not only a door to SQLi, it can also be confusing to developers about which is what, adding human errors to the attackers' ammunition.&lt;/p&gt;

&lt;h4&gt;
  
  
  Java DSL
&lt;/h4&gt;

&lt;p&gt;Another mitigation is to switch to what I call "DSL-first" approach (as opposed to "SQL-first", where you write real SQL.&lt;/p&gt;

&lt;p&gt;So instead of &lt;code&gt;select id, name from users where name like '%foo%'&lt;/code&gt;, you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select(ID, NAME).from(USERS).where(NAME.like("%" + name + "%"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The upside is that the DSL won't accept arbitrary concatenated string. And because the DSL result is a Java object, you can compose them, with both the subquery SQL &lt;strong&gt;and its parameters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The downsides however are also pretty glaring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You write in Java DSL, &lt;strong&gt;NOT SQL&lt;/strong&gt;. You can't easily copy and paste between your Java source code and your DB console to debug and test your SQL.&lt;/li&gt;
&lt;li&gt;You can't easily parameterize by table name or column names, unless you make an escape hatch, which then leaves the door to injection widely open.&lt;/li&gt;
&lt;li&gt;While IDE auto-completion feels nice for Java devs, when the SQL grows long and complex, it's more mentally taxing to have to first translate the DSL into SQL in your head before you can reason about it (it's leaky abstraction).&lt;/li&gt;
&lt;li&gt;Quirks like operator precedence divergence (&lt;code&gt;a.or(b).and(c)&lt;/code&gt; reads like &lt;code&gt;a OR b AND c&lt;/code&gt; but is &lt;em&gt;different&lt;/em&gt;) can be confusing and dangerous-they result in silent behavior bug and may cause data corruption.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;SafeSql&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before we dive into details, here's an intuitive example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SafeSql sql = SafeSql.of(
    "select id, name from users where name like '%{name}%'",
    name);
List&amp;lt;User&amp;gt; users = sql.query(dataSource, User.class);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How Does &lt;code&gt;SafeSql&lt;/code&gt; Prevent SQLi?
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;SafeSql&lt;/code&gt; is heavily influenced by Google's &lt;code&gt;TrustedSqlString&lt;/code&gt; technique: it uses ErrorProne to ensure only compile-time constants can be used as the SQL template.&lt;/p&gt;

&lt;p&gt;But it takes one step further by encapsulating the parameters with the sql template as a higher-level &lt;code&gt;SafeSql&lt;/code&gt; object that can be passed around and composed, kinda like &lt;a href="https://openjdk.org/jeps/459" rel="noopener noreferrer"&gt;JEP 459&lt;/a&gt;'s SQL processor.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SafeSql&lt;/code&gt; then enforces that all &lt;em&gt;value&lt;/em&gt; parameters are passed through &lt;code&gt;PreparedStatement&lt;/code&gt;'s parameterization.&lt;/p&gt;

&lt;p&gt;The only types of parameters that are directly embedded in the SQL string are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Another &lt;code&gt;SafeSql&lt;/code&gt; object, which is already validated to be safe from inection.&lt;/li&gt;
&lt;li&gt;backtick-quoted or double-quoted placeholders like &lt;code&gt;"{user_table}"&lt;/code&gt; are validated to only include characters that can be safely used as an identifier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is simply no way for an unsuspecting developer to accidentally inject malicious code. If &lt;code&gt;SafeSql&lt;/code&gt; compiles and runs, it's provably safe from SQLi.&lt;/p&gt;

&lt;p&gt;Next let's look at a few reasl world examples&lt;/p&gt;




&lt;h4&gt;
  
  
  Dynamic &lt;code&gt;IN&lt;/code&gt; Clauses with Collections
&lt;/h4&gt;

&lt;p&gt;When using an &lt;code&gt;IN&lt;/code&gt; clause, most frameworks require you to manually construct the right number of placeholders, which can be cumbersome and error-prone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (MyBatis XML):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- Using MyBatis &amp;lt;foreach&amp;gt; to handle dynamic IN lists --&amp;gt;
&amp;lt;select id="findByDepartmentIds" resultType="User"&amp;gt;
  SELECT * FROM Sales
  WHERE department_id IN
  &amp;lt;foreach item="id" collection="departmentIds" open="(" separator="," close=")"&amp;gt;
    #{id}
  &amp;lt;/foreach&amp;gt;
&amp;lt;/select&amp;gt;

// Called with:
sqlSession.selectList("findByDepartmentIds", Map.of("departmentIds", List.of(1, 2, 3)));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's safe from SQL injection (if you correctly used &lt;code&gt;#{}&lt;/code&gt;, not &lt;code&gt;${}&lt;/code&gt;). But it requires the special &lt;code&gt;&amp;lt;foreach&amp;gt;&lt;/code&gt; XML syntax, which adds visual clutter and complexity compared to writing plain SQL. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With SafeSql:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; departmentIds = List.of(1, 2, 3);
SafeSql.of(
    "SELECT * FROM Sales WHERE department_id IN ({department_ids})",
    departmentIds);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SafeSql automatically expands the list parameter &lt;code&gt;{department_ids}&lt;/code&gt; to the correct number of placeholders and binds the values, eliminating manual string work and preventing common mistakes.&lt;/p&gt;




&lt;h4&gt;
  
  
  Conditional Query Fragments
&lt;/h4&gt;

&lt;p&gt;Adding optional filters or groupings usually means string concatenation or awkward branching in your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (with manual concatenation):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String sql = "SELECT * FROM Sales";
if (groupByDay) {
    sql += " GROUP BY department_id, date";
} else {
    sql += " GROUP BY department_id";
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Manually stitching together query fragments can quickly become hard to read and maintain, and makes errors or injection bugs more likely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With SafeSql:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SafeSql.of(
    """
    SELECT department_id {group_by_day -&amp;gt; , date}, COUNT(*) AS cnt
    FROM Sales
    WHERE department_id IN ({department_ids})
    GROUP BY department_id {group_by_day -&amp;gt; , date},
    """,
    groupByDay, departmentIds, groupByDay);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-&amp;gt;&lt;/code&gt; guard in &lt;code&gt;{group_by_day -&amp;gt; , date}&lt;/code&gt; tells SafeSql to conditionally include &lt;code&gt;, date&lt;/code&gt; wherever needed based on the value of &lt;code&gt;groupByDay&lt;/code&gt;. This keeps dynamic queries clean and makes logic explicit at the SQL level, not hidden in string operations.&lt;/p&gt;




&lt;h4&gt;
  
  
  Injecting Identifiers like Column or Table Names
&lt;/h4&gt;

&lt;p&gt;Most frameworks only allow safe parameterization for values, because the JDBC &lt;code&gt;PreparedStatement&lt;/code&gt; only supports value parameterization. When it comes to parameterizing SQL identifiers, such as table names or column names, developers are forced to fall back to direct string concatenation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (in MyBatis XML):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;select id="findMetrics" resultType="Map"&amp;gt;
  SELECT department_id, date, ${metricColumns}
  FROM Sales
&amp;lt;/select&amp;gt;

// When called as:
sqlSession.selectList("findMetrics", Map.of("metricColumns", "price, currency, discount"));
// Generates:
SELECT department_id, date, price, currency, discount FROM Sales
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In MyBatis, &lt;code&gt;#{}&lt;/code&gt; is for safe value parameterization—these become JDBC parameters and are properly escaped.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;${}&lt;/code&gt; is direct string substitution—&lt;strong&gt;raw and unescaped&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;${metricColumns}&lt;/code&gt; means &lt;em&gt;whatever string is supplied&lt;/em&gt; is injected as-is into the SQL.

&lt;ul&gt;
&lt;li&gt;This creates a &lt;strong&gt;serious SQL injection risk&lt;/strong&gt; if any part of the input comes from user data.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The similarity between &lt;code&gt;#{}&lt;/code&gt; (JDBC parameterized) and &lt;code&gt;${}&lt;/code&gt; (please inject me!) is a frequent source of confusion and mistakes.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;With SafeSql:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Simply backtick-quote (&lt;strong&gt;or double-quote&lt;/strong&gt;, whichever your DB uses to quote identifiers) the placeholder and SafeSql will recognize the intention to use it as an identifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;List&amp;lt;String&amp;gt; metricColumns = List.of("price", "currency", "discount");
SafeSql.of("SELECT department_id, date, `{metric_columns}` FROM Sales", metricColumns);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SafeSql expands &lt;code&gt;{metric_columns}&lt;/code&gt; into a comma-separated, properly quoted, and validated list of identifiers. Unsafe or invalid names are rejected, which removes a whole class of injection risks and makes the query easy to audit.&lt;/p&gt;




&lt;h4&gt;
  
  
  Proper Escaping for LIKE Patterns
&lt;/h4&gt;

&lt;p&gt;When user input is used in &lt;code&gt;LIKE&lt;/code&gt; queries, forgetting to escape &lt;code&gt;%&lt;/code&gt; or &lt;code&gt;_&lt;/code&gt; can cause accidental matches or security issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (with manual escaping):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String term = userInput.replace("%", "\%").replace("_", "\_");
String sql = "SELECT * FROM users WHERE name LIKE ?";
jdbcTemplate.query(sql, term);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Manual escaping is repetitive and easy to forget, leading to unpredictable results or vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With SafeSql:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SafeSql.of("SELECT * FROM users WHERE name LIKE '%{name}%'", userName);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SafeSql escapes any special characters in parameters used within &lt;code&gt;LIKE&lt;/code&gt; expressions automatically. You don’t have to think about escaping rules or risk mistakes—user input is always treated literally.&lt;/p&gt;




&lt;h4&gt;
  
  
  Query Composition and Parameter Isolation
&lt;/h4&gt;

&lt;p&gt;Composing queries from reusable fragments often leads to parameter name clashes or incorrect bindings, especially as code grows.&lt;/p&gt;

&lt;p&gt;With manual tracking, you have to carefully manage names and argument lists, often by inventing your own conventions, which makes code hard to read and can break during refactorings.&lt;/p&gt;

&lt;p&gt;It’s easy to accidentally overwrite or mismatch parameters, leading to subtle bugs or unexpected results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With SafeSql:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SafeSql computation = SafeSql.of("CASE ... END as computed, {from_time}", fromTime);
SafeSql whereClause = ...;
SafeSql.of(
    """
    SELECT department_id, {computation}
    FROM Sales
    WHERE {where_clause}
    """,
    computation, whereClause);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each fragment’s parameters are managed in isolation by SafeSql, so you can safely compose and reuse query parts without worrying about accidental overwrites or binding mistakes. They are guaranteed to be eventually sent through &lt;code&gt;PreparedStatement&lt;/code&gt; in the correct order.&lt;/p&gt;




&lt;h4&gt;
  
  
  Parameter Name and Order Checking
&lt;/h4&gt;

&lt;p&gt;In traditional frameworks, parameter order or name mismatches can slip by the compiler and only fail at runtime—or worse, silently produce incorrect results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (with JDBC):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String sql = "SELECT * FROM users WHERE id = ? AND name = ?";
preparedStatement.setInt(1, userName); // mistake: wrong order
preparedStatement.setString(2, userId);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example (with Spring NamedParameterJdbcTemplate):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new NamedParameterJdbcTemplate(dataSource)
    .queryForList(
        "SELECT * FROM users WHERE id = :id AND name = :name",
        Map.of("nmae", userName, "id", userId));  // typo!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of error won’t always be caught during development, and can be difficult to debug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With SafeSql:&lt;/strong&gt;&lt;br&gt;
SafeSql integrates with ErrorProne to check at compile time that the template placeholders and the template arguments match. If you get the order or number of arguments wrong, you’ll get a clear compilation error.&lt;/p&gt;

&lt;p&gt;For example, the following code will fail to compile because the order of the two arguments is wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SafeSql.of(
    """
    select id, name, age from users
    where id = {id} OR name LIKE '%{name}%'
    """,
    userName, userId);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such compile-time check is very useful when your SQL is large and you need to change or move a snippet around, which can cause the placeholders no longer matching the template arguments.&lt;/p&gt;

&lt;p&gt;Sometimes you may run into a placeholder name that doesn't match the corresponding argument expression. If renaming the placeholder isn't an option, consider using an explicit comment like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SafeSql.of(
    """
    select id, name, age
    from users
    where id = {user_id} OR name LIKE '%{user_name}%'
    """,
    /* user_id */ userRequest.getUuid(), userRequest.getUserName());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The explicit &lt;code&gt;/* user_id */&lt;/code&gt; confirms your intention to both SafeSql &lt;strong&gt;and to your readers&lt;/strong&gt; that you do mean to use the uuid as the &lt;code&gt;user_id&lt;/code&gt;. &lt;/p&gt;




&lt;h4&gt;
  
  
  Type Safety
&lt;/h4&gt;

&lt;p&gt;SafeSql doesn't know your database schema so cannot check for column types or column name typos.&lt;/p&gt;

&lt;p&gt;But typos and superficial type mismatch (like using int for the &lt;code&gt;name&lt;/code&gt; column) are the easy kind of errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you test your SQL (and you should), the DB will instantly catch these errors for you.

&lt;ul&gt;
&lt;li&gt;In fact, it's best if you've copied the SQL from the DB console where the correctness is already verified.&lt;/li&gt;
&lt;li&gt;And you should have automated tests to ensure the SQL not only type checks, but gives you the right result.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Most db types are numbers and strings. So the type checking can only provide marginal protection anyways.&lt;/li&gt;

&lt;li&gt;False sense of security is dangerous. If you ever wish to skip testing your SQL because it "type checks",
then it's a liability.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The main safety advantages of SafeSql are in its &lt;strong&gt;zero-backdoor&lt;/strong&gt; SQL injection prevention, compile-time &lt;strong&gt;semantic check&lt;/strong&gt; such that you can't pass &lt;code&gt;user.name()&lt;/code&gt; in the place of &lt;code&gt;{ssn}&lt;/code&gt; despite both being string, as well as automatic parameter wiring, and convenient dynamic query construction—areas where mistakes are easy to make and hard to find otherwise.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In a nutshell, use &lt;code&gt;SafeSql&lt;/code&gt; if any of the following applies to you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are a large enterprise. Relying on developer vigilance to avoid SQL injection isn't an option. Instead, you need &lt;strong&gt;systematically enforced safety&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You prefer to write actual SQL, and appreciate the ability to directly &lt;strong&gt;copy-paste queries&lt;/strong&gt; between your code and the database console for easy debugging and testing.&lt;/li&gt;
&lt;li&gt;A low learning curve and a &lt;em&gt;what-you-see-is-what-you-get&lt;/em&gt; (WYSIWYG) approach are important to you. No Domain Specific Language (DSL) to learn.&lt;/li&gt;
&lt;li&gt;You need to parameterize by &lt;strong&gt;table names&lt;/strong&gt;, &lt;strong&gt;column names&lt;/strong&gt;, all while preventing injection risks.&lt;/li&gt;
&lt;li&gt;You have &lt;strong&gt;dynamic and complex subqueries&lt;/strong&gt; to compose. And you find it error prone managing the subquery parameters manually.&lt;/li&gt;
&lt;li&gt;You value &lt;strong&gt;compile-time semantic error prevention&lt;/strong&gt; so you won't accidentally use &lt;code&gt;user.name()&lt;/code&gt; in a place intended for &lt;code&gt;{ssn}&lt;/code&gt;, even if both are strings.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sqlinjection</category>
      <category>dynamicsql</category>
      <category>preparedstatement</category>
      <category>jdbc</category>
    </item>
    <item>
      <title>Designing a Better String Utility - Part 2</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Thu, 19 Jun 2025 16:44:20 +0000</pubDate>
      <link>https://dev.to/fluentfuture/designing-a-better-string-utility-4k49</link>
      <guid>https://dev.to/fluentfuture/designing-a-better-string-utility-4k49</guid>
      <description>&lt;h2&gt;
  
  
  Part 2: Designing a Better String Utility
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/fluentfuture/designing-a-better-string-util-part-1-2ehe"&gt;Part 1&lt;/a&gt;, we saw that everyday string manipulation is surprisingly brittle. So let’s try designing something better—from scratch.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔍 Observation
&lt;/h3&gt;

&lt;p&gt;Almost every string operation we need can be broken down into a few orthogonal concerns:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;Where is the substring?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The number of choices is manageable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the first &lt;code&gt;"."&lt;/code&gt; (think of &lt;code&gt;String.indexOf()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;the last &lt;code&gt;"/"&lt;/code&gt; (&lt;code&gt;String.lastIndexOf()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;the prefix &lt;code&gt;"root/"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the suffix &lt;code&gt;".html"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a regular expression match&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;How do we navigate from the match?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Once we find it, we might want the:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;part &lt;em&gt;before&lt;/em&gt; it&lt;/li&gt;
&lt;li&gt;part &lt;em&gt;after&lt;/em&gt; it&lt;/li&gt;
&lt;li&gt;substring &lt;em&gt;up to&lt;/em&gt; the end of the match&lt;/li&gt;
&lt;li&gt;substring from the match to the end of the string&lt;/li&gt;
&lt;li&gt;the content &lt;em&gt;between&lt;/em&gt; two patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;Do we want &lt;em&gt;a&lt;/em&gt; occurrence, or all?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Sometimes we just want the first. Other times, we want to find all occurrences.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. &lt;strong&gt;What do we want to do with it?&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Extract&lt;/li&gt;
&lt;li&gt;Remove&lt;/li&gt;
&lt;li&gt;Replace&lt;/li&gt;
&lt;li&gt;Split around the occurrence (result in two parts)&lt;/li&gt;
&lt;li&gt;Split around all occurrences (result in a stream or list)&lt;/li&gt;
&lt;li&gt;Or just get the raw indices&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  📐 Step 1: The Substring as a Range
&lt;/h3&gt;

&lt;p&gt;No matter how we find it, a substring is just a range: &lt;code&gt;[begin, end)&lt;/code&gt;. This is exactly how &lt;code&gt;String.substring(int, int)&lt;/code&gt; works.&lt;/p&gt;

&lt;p&gt;So let’s create a simple class to model it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fullString&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fullString&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fullString&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;replaceWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@Override&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fullString&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Already, we can add some trivial but useful &lt;code&gt;before()&lt;/code&gt;, &lt;code&gt;after()&lt;/code&gt;, &lt;code&gt;remove()&lt;/code&gt;, and &lt;code&gt;replaceWith()&lt;/code&gt; helpers.&lt;/p&gt;

&lt;p&gt;Note: while the record looks extremely simple, it's the most important aspect of this design: all the extensibility and composability are centered around this simple concept: they are strategies to find, to navigate and to act on a range.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧭 Step 2: Finding a Substring
&lt;/h3&gt;

&lt;p&gt;We need an abstraction for “a way to find a Substring in a given input.” Let's call it &lt;code&gt;Pattern&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;indexOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
      &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// almost same as first(), except use lastIndexOf()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="nf"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
      &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="nf"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// very similar to prefix()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The static methods such as &lt;code&gt;first()&lt;/code&gt;, &lt;code&gt;last()&lt;/code&gt;, &lt;code&gt;prefix()&lt;/code&gt; etc. correspond to the first observation (where to find).&lt;/p&gt;

&lt;p&gt;The  find() method returns an Optional, and this design principle applies across the API: all substring extraction methods return Optional. This ensures that absence is explicit and must be handled consciously, avoiding the kind of implicit fallbacks or silent failures seen in methods like substringAfter() and substringBefore().&lt;/p&gt;

&lt;p&gt;We also let the &lt;code&gt;find()&lt;/code&gt; method take a secondary &lt;code&gt;int fromIndex&lt;/code&gt; parameter to facilitate chaining, which you’ll see shortly.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧭 Step 3: Using the Found Substring
&lt;/h3&gt;

&lt;p&gt;As we've seen, there are many things we might want to do once we've found a substring.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Substring&lt;/code&gt; record provides the index and some basic helper methods like &lt;code&gt;remove()&lt;/code&gt; and &lt;code&gt;replaceWith()&lt;/code&gt;. But we can also add higher-level methods to &lt;code&gt;Pattern&lt;/code&gt; to directly perform common operations like extraction, removal, replacement, and splitting—without requiring the caller to explicitly invoke &lt;code&gt;find()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Substring:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;removeFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Substring:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;replaceFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BiFunction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;before&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;after&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These helpers let us perform removal, replacement, and splitting on any of the &lt;code&gt;first()&lt;/code&gt;, &lt;code&gt;last()&lt;/code&gt;, &lt;code&gt;prefix()&lt;/code&gt;, or &lt;code&gt;suffix()&lt;/code&gt; patterns in a consistent and predictable way.&lt;/p&gt;

&lt;p&gt;The return value of &lt;code&gt;from()&lt;/code&gt; is &lt;code&gt;Optional&lt;/code&gt; to force the caller to explicitly handle missing matches—avoiding assumptions like those made by &lt;code&gt;substringAfter()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;removeFrom()&lt;/code&gt; and &lt;code&gt;replaceFrom()&lt;/code&gt; methods don’t return &lt;code&gt;Optional&lt;/code&gt; because, idiomatically, removal is a no-op if the pattern isn’t found (just like &lt;code&gt;Collection.remove()&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  🧭 Step 4: Composition and Navigation
&lt;/h3&gt;

&lt;p&gt;We’re still missing an important part of the API: how to express operations like &lt;code&gt;before(first(foo))&lt;/code&gt;, &lt;code&gt;after(last(bar))&lt;/code&gt;, or &lt;code&gt;between(a, b)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can build this via composition. This is also where the &lt;code&gt;int fromIndex&lt;/code&gt; parameter to &lt;code&gt;find()&lt;/code&gt; becomes useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;begin&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="nf"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromIndex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                          &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;begin&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                      &lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="nf"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;before(p)&lt;/code&gt; pattern applies &lt;code&gt;p&lt;/code&gt; and returns everything to the left; &lt;code&gt;after(p)&lt;/code&gt; returns everything to the right.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;between(a, b)&lt;/code&gt; pattern first applies &lt;code&gt;a&lt;/code&gt;, then applies &lt;code&gt;b&lt;/code&gt; starting from the end of &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can use the same strategy to build other navigational patterns like &lt;code&gt;upToIncluding(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The overload &lt;code&gt;between(String, String)&lt;/code&gt; is just a convenience method for common cases like &lt;code&gt;between("(", ")")&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✅ How Are We Doing?
&lt;/h3&gt;

&lt;p&gt;Let’s revisit the Part 1 use cases.&lt;/p&gt;

&lt;p&gt;We can now express many earlier problems using simple, composable code:&lt;/p&gt;

&lt;h4&gt;
  
  
  Extracting the extension name:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myfile"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike &lt;code&gt;substringAfter()&lt;/code&gt;, the fallback logic is explicit—no room for error.&lt;/p&gt;

&lt;h4&gt;
  
  
  Getting the directory:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'/'&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myfile"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// → ""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, it forces you to be explicit—you cannot forget.&lt;/p&gt;

&lt;h4&gt;
  
  
  Getting directory path between root and last "/":
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"home/usr/"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"home/usr/module/component/file.txt"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
     &lt;span class="c1"&gt;// → "module/component"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Split a key-value pair
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"k1=v1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;Pair:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → Optional[Pair("k1", "v1")]&lt;/span&gt;

&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"k1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;Pair:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → Optional.empty()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🔁 Step 5: Repeating Pattern
&lt;/h3&gt;

&lt;p&gt;Sometimes we want to apply the same pattern repeatedly. That’s where &lt;code&gt;RepeatingPattern&lt;/code&gt; comes in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;RepeatingPattern&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Substring&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Substring&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;replaceAllFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;StringBuilder&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StringBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Substring&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;begin&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;removeAllFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;replaceAllFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{...}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With pretty straightforward implementation code, we've added useful "acting" functionalities for all the previously defined substring patterns.&lt;/p&gt;

&lt;p&gt;You can also implement the n-way &lt;code&gt;split()&lt;/code&gt; using the results from &lt;code&gt;findAll()&lt;/code&gt;. We omit it here for brevity.&lt;/p&gt;

&lt;p&gt;For ergonomic chaining, let’s add a &lt;code&gt;repeatedly()&lt;/code&gt; helper to &lt;code&gt;Pattern&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="nc"&gt;RepeatingPattern&lt;/span&gt; &lt;span class="nf"&gt;repeatedly&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RepeatingPattern&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ What Have We Accomplished?
&lt;/h3&gt;

&lt;p&gt;We've built a library with pretty simple API, yet with sufficient flexibility to cover diverse everyday String use cases.&lt;/p&gt;

&lt;p&gt;This is possible because we've decoupled orthogonal concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;first()&lt;/code&gt;, &lt;code&gt;last()&lt;/code&gt;, &lt;code&gt;prefix()&lt;/code&gt;, &lt;code&gt;suffix()&lt;/code&gt; and friends do one thing only: find the range.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;before()&lt;/code&gt;, &lt;code&gt;after()&lt;/code&gt;, &lt;code&gt;between()&lt;/code&gt;, &lt;code&gt;upToIncluding()&lt;/code&gt;, &lt;code&gt;toEnd()&lt;/code&gt; do one thing only: compose from simpler patterns to more sophisticated patterns.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.from(String)&lt;/code&gt;, &lt;code&gt;.split(String)&lt;/code&gt;, &lt;code&gt;.replaceFrom(String)&lt;/code&gt; etc. do one thing only: given any simple or composite pattern, perform extraction, splitting, replacing from the range.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.repeatedly()&lt;/code&gt; turns any pattern into a &lt;code&gt;RepeatingPattern&lt;/code&gt;, and then provide operations on all occurrences of a pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each individual family of methods are relatively straightforward, but combined together, they can perform far more sophisticated string operation.&lt;/p&gt;

&lt;p&gt;Below, let's take a look at how we can use a combination to solve a more complex problem.&lt;/p&gt;




&lt;h4&gt;
  
  
  Split multiple key-values from a structured map string
&lt;/h4&gt;

&lt;p&gt;This example shows how to compose &lt;code&gt;Pattern&lt;/code&gt; and &lt;code&gt;RepeatingPattern&lt;/code&gt; to achieve a relatively complex task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{k1=v1, k2=v2, k3=v3}"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;repeatedly&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nl"&gt;Map:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Entry:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;Entry:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// → {k1=v1, k2=v2, k3=v3}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what’s happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use &lt;code&gt;between("{", "}")&lt;/code&gt; to extract the content.&lt;/li&gt;
&lt;li&gt;Then we split it into key-value strings using &lt;code&gt;first(",").repeatedly()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally, each key-value string is split again by &lt;code&gt;=&lt;/code&gt; into a &lt;code&gt;Map.Entry&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This API lets you build robust and extremely flexible string manipulation logic without worrying about magic offsets, nulls, or off-by-one errors—all with predictable failure behavior.&lt;/p&gt;

&lt;p&gt;We've covered the basics of the &lt;code&gt;Substring&lt;/code&gt; API design.&lt;/p&gt;

&lt;p&gt;The actual library is extensively used in Google's internal codebase and includes many more features such as look-around support (&lt;code&gt;.immediatelyBetween(...)&lt;/code&gt;, &lt;code&gt;followedBy(...)&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://github.com/google/mug/blob/master/mug/src/main/java/com/google/mu/util/Substring.java" rel="noopener noreferrer"&gt;Git Repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>string</category>
      <category>design</category>
    </item>
    <item>
      <title>Designing a Better String Utility - Part 1</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Wed, 18 Jun 2025 16:20:36 +0000</pubDate>
      <link>https://dev.to/fluentfuture/designing-a-better-string-util-part-1-2ehe</link>
      <guid>https://dev.to/fluentfuture/designing-a-better-string-util-part-1-2ehe</guid>
      <description>&lt;h2&gt;
  
  
  Simple Things Not Easy
&lt;/h2&gt;

&lt;p&gt;Finding a substring is the most basic text operation in any language. And yet in Java, even Kotlin, is surprisingly awkward and error-prone.&lt;/p&gt;

&lt;p&gt;Consider the following example that finds the extension name of a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"myfile"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substringAfter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file actually has no extension name, but the code returns &lt;code&gt;myfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's odd?!&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; explicitly choose a different default behavior. But that's the point:, the &lt;strong&gt;default behavior is confusing&lt;/strong&gt; and it &lt;em&gt;will&lt;/em&gt; bite you!&lt;/p&gt;

&lt;p&gt;What makes things worse, is if you are used to Apache's StringUtils, you may have expected differently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substringAfter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myfile"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it says the extension name is empty. Seems to make more sense?&lt;/p&gt;

&lt;p&gt;So, should Kotlin's String API have followed &lt;code&gt;StringUtils&lt;/code&gt;? &lt;/p&gt;

&lt;p&gt;Not quite. Let's study the following &lt;code&gt;StringUtils&lt;/code&gt; example:&lt;/p&gt;




&lt;h3&gt;
  
  
  🔥 Extracting the Directory Path
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substringBeforeLast&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;filename&lt;/code&gt; is &lt;code&gt;"path/to/file"&lt;/code&gt;, the code will extract &lt;code&gt;"path/to"&lt;/code&gt; as the directory.&lt;/p&gt;

&lt;p&gt;But what happens if &lt;code&gt;filename&lt;/code&gt; is merely &lt;code&gt;"myfile"&lt;/code&gt;? Interestingly, StringUtils decides to use a different strategy for the "not found" case: to fall back to the original string, which is &lt;code&gt;"myfile"&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;But &lt;code&gt;"myfile"&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; the directory path!&lt;/p&gt;

&lt;p&gt;At least Kotlin is consistent with itself to always fall back to the original string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;directory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substringBeforeLast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// → "myfile"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we call an extraction method, we're asking for something specific. If the thing doesn't exist, quietly pretending it does is misleading—and confusing.  &lt;/p&gt;

&lt;p&gt;Worse, the fallback behavior is inconsistent across methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;substringBefore()&lt;/code&gt; → returns the &lt;strong&gt;original string&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;substringAfter()&lt;/code&gt; → returns an &lt;strong&gt;empty string&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;substringBetween()&lt;/code&gt; → returns &lt;strong&gt;null&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same type of failure. Three different, silent error results.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔥 Extracting Path Under Root
&lt;/h3&gt;

&lt;p&gt;Given file path &lt;code&gt;"home/usr/path/to/file.txt"&lt;/code&gt;, we want to extract the &lt;code&gt;"path/to"&lt;/code&gt; part after the known &lt;code&gt;"home/usr/"&lt;/code&gt; prefix:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kotlin:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;directory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removePrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"home/usr/"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substringBeforeLast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Apache Commons:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substringBeforeLast&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeStart&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"root/"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, the happy path seems fine. But what happens if &lt;code&gt;path&lt;/code&gt; doesn't start with &lt;code&gt;/home/usr/&lt;/code&gt;? Or if it doesn't have another &lt;code&gt;/&lt;/code&gt; after the prefix? Say, it's just &lt;code&gt;myfile&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;As discussed earlier, the "path under root" then becomes &lt;code&gt;"myfile"&lt;/code&gt;. Not intuitive, and readers will likely need to look up the document to tell.&lt;/p&gt;

&lt;p&gt;Let's look at a few other real world use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔥 Splitting a Key-Value Pair
&lt;/h3&gt;

&lt;p&gt;Given the following HTTP line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Authorization: Bearer token"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How do you get the header name and value?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kotlin:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;parts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;          &lt;span class="c1"&gt;// → "Authorization"&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOrNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;  &lt;span class="c1"&gt;// → "Bearer token"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Apache Commons:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// → "Authorization"&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// → "Bearer token"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that if you don't carefully check the result array's length, you  will run into exceptions when the input string doesn't include the colon (&lt;code&gt;:&lt;/code&gt;) character.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔥 Parsing Multiple Key-Value Pairs
&lt;/h3&gt;

&lt;p&gt;What if I want to parse a dictionary-like input string such as the following?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{k1=v1, k2=v2, k3=kv}"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Kotlin:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;raw&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeSurrounding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;map&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mapNotNull&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;kv&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;toMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Apache Commons:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;trimmed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeStart&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;trimmed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeEnd&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trimmed&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trimmed&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]),&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither the Kotlin code or the Apache StringUtils code is convenient or easy to read.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Is It So Hard?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Error Handling
&lt;/h3&gt;

&lt;p&gt;One-off util methods tend to be arbitrary in their error handling strategy, as discussed above.&lt;/p&gt;

&lt;p&gt;It's tempting to pick a seemingly okay default value in order to keep the method signature "simple", at the cost of potential confusions.&lt;/p&gt;

&lt;p&gt;And with arbitrary choice of default values, it can easily get inconsistent, making it even harder for people to remember.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. One Method Per Use Case? That Doesn’t Scale
&lt;/h3&gt;

&lt;p&gt;Every real-world string task feels slightly different.&lt;br&gt;&lt;br&gt;
But writing a separate method for each case quickly gets out of hand.&lt;/p&gt;

&lt;p&gt;That’s why Apache Commons StringUtils has over &lt;strong&gt;200 public static methods&lt;/strong&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;splitByWholeSeparatorPreserveAllTokens(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;splitByCharacterTypeCamelCase(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These method names are overly long, to the point that you might not be able to intuitively &lt;em&gt;get&lt;/em&gt; what they do. They are narrowly scoped to a specific use case that you have to carefully read the javadoc to understand.&lt;/p&gt;

&lt;p&gt;They are rarely composable and their edge cases can sometimes be surprising.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. The Static Util Class Trap
&lt;/h3&gt;

&lt;p&gt;StringUtils mixes up two separate concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How&lt;/strong&gt; to find something in a string
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What&lt;/strong&gt; to do with it once found&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of composition, you get a mess of methods with long names and adhoc behavior. It's why util classes are often considered an anti-pattern.&lt;/p&gt;

&lt;p&gt;What we really need is a way to model &lt;em&gt;finding&lt;/em&gt; and &lt;em&gt;acting&lt;/em&gt; separately.&lt;/p&gt;

&lt;p&gt;In Part 2, we’ll try to build around a simple abstraction. And we'll see if a different approach can result in better coverage, with a simpler API.&lt;/p&gt;

</description>
      <category>java</category>
      <category>kotlin</category>
      <category>string</category>
    </item>
    <item>
      <title>The Easiest String Parsing in Java</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Wed, 21 May 2025 14:37:39 +0000</pubDate>
      <link>https://dev.to/fluentfuture/parsing-structured-strings-in-java-for-beginners-2j53</link>
      <guid>https://dev.to/fluentfuture/parsing-structured-strings-in-java-for-beginners-2j53</guid>
      <description>&lt;p&gt;Parsing structured strings in Java has always been painful. Most developers reach for regular expressions, &lt;code&gt;split()&lt;/code&gt;, or manual slicing. But these techniques are error-prone, hard to read, and most importantly—&lt;strong&gt;unsafe at compile time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;StringFormat&lt;/code&gt; class makes parsing so easy that even a beginner can implement with a one-liner.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Regex is Not Easy
&lt;/h2&gt;

&lt;p&gt;Take a common use case: parsing a file path like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/logs/2024/05/16/system.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want to extract year, month, day, and filename. Here’s what most people do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="no"&gt;LOG_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"/logs/(\\d{4})/(\\d{2})/(\\d{2})/(.+)\\.log"&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// elsewhere in code:&lt;/span&gt;
&lt;span class="nc"&gt;Matcher&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LOG_PATH&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Regex pattern readability sucks.&lt;/li&gt;
&lt;li&gt;Use group names and your pattern readability sucks even more.&lt;/li&gt;
&lt;li&gt;With pattern and extraction logic far apart, you could get the groups out of order.&lt;/li&gt;
&lt;li&gt;Group indices are magic numbers.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ✅ Structured Parsing with StringFormat
&lt;/h2&gt;

&lt;p&gt;Tthe same logic using &lt;code&gt;StringFormat&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;StringFormat&lt;/span&gt; &lt;span class="no"&gt;FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/logs/{year}/{month}/{day}/{file}.log"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="no"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseOrThrow&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"/logs/2024/05/16/system.log"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. No string math, no group numbers.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛡️ Compile-time Safety
&lt;/h2&gt;

&lt;p&gt;StringFormat performs compile-time validation of the lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Error: parameter count mismatch&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{a}-{b}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseOrThrow&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1-2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;//                       ~~~~~~~~~~~~~~~&lt;/span&gt;
&lt;span class="c1"&gt;//                       Compilation error: too many parameters&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Error: parameter order mismatch&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{a}-{b}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseOrThrow&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1-2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;//                       ~~~~~~~&lt;/span&gt;
&lt;span class="c1"&gt;//                       Compilation error: expected order (a, b)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Correct: order matches field declaration&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{a}-{b}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseOrThrow&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1-2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;...);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This level of safety is &lt;strong&gt;not possible&lt;/strong&gt; with regex, &lt;code&gt;split()&lt;/code&gt;, or ad-hoc parsing.&lt;/p&gt;

&lt;p&gt;No need to “remember” group indices or keep documentation in sync with code—the compiler checks it for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Scanning repeatedly
&lt;/h2&gt;

&lt;p&gt;Say, if you have many such file paths in the input string, you can &lt;em&gt;lazily&lt;/em&gt; scan them all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;...)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(...)&lt;/span&gt;  &lt;span class="c1"&gt;// apply whatever filtering you care about&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;// if you want up to 10 such files&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ✅ Regex is Unpredictable
&lt;/h2&gt;

&lt;p&gt;Regex engines in Java (and many other platforms) use &lt;a href="https://swtch.com/%7Ersc/regexp/regexp1.html" rel="noopener noreferrer"&gt;&lt;strong&gt;NFA-based backtracking&lt;/strong&gt;&lt;/a&gt;, which means certain patterns (especially involving nested repetitions or ambiguous alternations) can cause &lt;em&gt;catastrophic slowdowns&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Even simple-looking regexes like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;href="([^"]*)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;((a|b|c)+)+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can trigger catastrophic backtracking when matched against malicious input like nested quotes or repeated characters. These aren’t toy examples—real-world systems have gone down because of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stack Overflow 2016&lt;/strong&gt;: a regex used to extract comment anchors caused a global outage due to backtracking explosion (&lt;a href="https://stackstatus.tumblr.com/post/147710624694/outage-postmortem-july-20-2016" rel="noopener noreferrer"&gt;postmortem&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare 2019&lt;/strong&gt;: a single WAF rule with a pathological regex caused CPU saturation and took down large parts of the network (&lt;a href="https://blog.cloudflare.com/cloudflare-outage/" rel="noopener noreferrer"&gt;incident report&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RE2, Google's safe regex engine, avoids this with DFA-based guarantees—but at the cost of expressive power (e.g., no backreferences or lookaround) and often slower than hand-written substring logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ StringFormat uses &lt;code&gt;indexOf()&lt;/code&gt; calls
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;StringFormat&lt;/code&gt; avoids regex entirely. It splits the template into static fragments and uses a &lt;strong&gt;left-to-right scan&lt;/strong&gt; driven by &lt;code&gt;String.indexOf(...)&lt;/code&gt; to locate placeholder matches.&lt;/p&gt;

&lt;p&gt;This gives several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Deterministic&lt;/strong&gt; linear scanning&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;No backtracking&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;No regex syntax or escaping&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Fast constant-time fragment matching&lt;/strong&gt; (in practice, &lt;code&gt;indexOf(...)&lt;/code&gt; is extremely fast for short needles)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Safe under adversarial input&lt;/strong&gt; (no regex ReDoS risk)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In real workloads, where fragments are small (&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;:&lt;/code&gt;, &lt;code&gt;@&lt;/code&gt;, etc.) and inputs are well-structured, &lt;code&gt;indexOf()&lt;/code&gt; &lt;strong&gt;outperforms regex&lt;/strong&gt; by a wide margin—often many times faster per match.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Summary
&lt;/h2&gt;

&lt;p&gt;If you're building tooling that parses structured text:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't use regex unless you need full pattern generality.&lt;/li&gt;
&lt;li&gt;Don’t trust that it will behave the same under scale or attack.&lt;/li&gt;
&lt;li&gt;Use simple substring matching where structure allows—like &lt;code&gt;StringFormat&lt;/code&gt; does.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ↔️ Bidirectional
&lt;/h2&gt;

&lt;p&gt;While parsing is the main use case, the same format string also supports formatting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The format string is always the source of truth. No string concatenation. No misplaced &lt;code&gt;.append()&lt;/code&gt; chains.&lt;/p&gt;

&lt;p&gt;Similar compile-time protection exists: you'll get compilation error if you pass in wrong number of args, or for example get the &lt;code&gt;file&lt;/code&gt; and &lt;code&gt;year/month/day&lt;/code&gt; in the wrong order.&lt;/p&gt;

&lt;p&gt;With traditional &lt;code&gt;String.format("input size for %s is %s", user.id(), input.size())&lt;/code&gt;, it's not a good idea to stash away the format string as a constant because then it's easy to pass the format arguments in the wrong order.&lt;/p&gt;

&lt;p&gt;But with &lt;code&gt;StringFormat&lt;/code&gt; and its compile-time check, it's safe to do so, making it easier to reuse the same format string at different places.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;format()&lt;/code&gt; uses direct string concatenation (&lt;code&gt;+&lt;/code&gt;), which is faster than &lt;code&gt;StringBuilder&lt;/code&gt; on Java 9 and higher.&lt;/p&gt;




&lt;h2&gt;
  
  
  🐍 Python's &lt;code&gt;parse&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Looking around, Python offers similar syntax in its &lt;a href="https://pypi.org/project/parse/" rel="noopener noreferrer"&gt;&lt;code&gt;parse&lt;/code&gt;&lt;/a&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/logs/{year}/{month}/{day}/{file}.log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/logs/2024/05/17/server.log&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# {'year': '2024', 'month': '05', 'day': '17', 'file': 'server'}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except &lt;code&gt;parse()&lt;/code&gt; offers &lt;strong&gt;no compile-time enforcements&lt;/strong&gt;, and under the hood is a wrapper of regex, so suffers the same NFA-based regex performance overhead and potential disastrous backtracking.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌍 Other Languages
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Closest Equivalent&lt;/th&gt;
&lt;th&gt;Readability&lt;/th&gt;
&lt;th&gt;Compile-Time Safety&lt;/th&gt;
&lt;th&gt;Bidirectional&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Java&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StringFormat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ High&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Template-based parsing with lambda + type safety&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ High&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Clean syntax, runtime-only verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JS/TS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;path-to-regexp&lt;/code&gt;, &lt;code&gt;match()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;⚠️ Medium&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;⚠️ Partial&lt;/td&gt;
&lt;td&gt;URL-focused, lacks general structure matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;regexp&lt;/code&gt; (manual group extraction)&lt;/td&gt;
&lt;td&gt;❌ Low&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Verbose and error-prone, no field names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C++&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;std::regex&lt;/code&gt;, manual parsing&lt;/td&gt;
&lt;td&gt;❌ Low&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;No built-in structure mapping; verbose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kotlin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Regex&lt;/code&gt;, manual destructuring&lt;/td&gt;
&lt;td&gt;⚠️ Medium&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;No declarative templates, no type checks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔍 Observations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; is the only language with syntax on par with &lt;code&gt;StringFormat&lt;/code&gt;, but all verification is deferred to runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript&lt;/strong&gt;, &lt;strong&gt;Go&lt;/strong&gt;, &lt;strong&gt;C++&lt;/strong&gt;, and &lt;strong&gt;Kotlin&lt;/strong&gt; rely on regex or ad-hoc logic without structural templates.&lt;/li&gt;
&lt;li&gt;Only &lt;strong&gt;Java + StringFormat&lt;/strong&gt; delivers &lt;strong&gt;template-based parsing&lt;/strong&gt; with &lt;strong&gt;lambda field binding&lt;/strong&gt;, and &lt;strong&gt;compiler-checked safety&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;







&lt;p&gt;👉 Github Repo: &lt;a href="https://github.com/google/mug/blob/master/mug/src/main/java/com/google/mu/util/README.md" rel="noopener noreferrer"&gt;Mug&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>parse</category>
      <category>regex</category>
      <category>string</category>
    </item>
    <item>
      <title>Off-by-1 again in your LeetCode binary search solution?</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Fri, 16 May 2025 04:11:25 +0000</pubDate>
      <link>https://dev.to/fluentfuture/off-by-one-again-in-your-leetcode-binary-search-solution-this-declarative-java-api-can-help-aeo</link>
      <guid>https://dev.to/fluentfuture/off-by-one-again-in-your-leetcode-binary-search-solution-this-declarative-java-api-can-help-aeo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Although the basic idea of binary search is straightforward, the details are surprisingly tricky...&lt;br&gt;&lt;br&gt;
In fact, binary search has been incorrectly implemented in textbooks, published code, and production software for decades.”&lt;br&gt;&lt;br&gt;
— Donald Knuth, &lt;em&gt;The Art of Computer Programming&lt;/em&gt;, Vol. 3&lt;/p&gt;

&lt;p&gt;“The truth is, very few correct versions have ever been published, at least in mainstream programming languages.”&lt;br&gt;&lt;br&gt;
— Joshua Bloch, &lt;a href="https://research.googleblog.com/2006/06/extra-extra-read-all-about-it-nearly.html" rel="noopener noreferrer"&gt;Google Research Blog (2006)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Binary search is a beautiful algorithm — but implementing it is full of sharp edges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;off-by-one errors&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lo &amp;lt;= hi&lt;/code&gt; or &lt;code&gt;lo &amp;lt; hi&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hi = mid&lt;/code&gt; or &lt;code&gt;hi = mid - 1&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;mid overflow&lt;/li&gt;
&lt;li&gt;loop doesn't stop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://google.github.io/mug/apidocs/com/google/mu/collect/BinarySearch.html" rel="noopener noreferrer"&gt;&lt;code&gt;BinarySearch&lt;/code&gt;&lt;/a&gt; class helps to abstract away those nuances.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 How it Works
&lt;/h2&gt;

&lt;p&gt;You provide a lambda that tells the algorithm which direction to search. Just return:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-1&lt;/code&gt; → search left&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; → search right&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; → stop (target found)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It follows the same contract as &lt;code&gt;Comparator&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;BinarySearch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forInts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Range&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertionPointFor&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;...)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;floor&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// or .ceiling()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⬇️ floor() or ⬆️ ceiling()?
&lt;/h2&gt;

&lt;p&gt;Once the search lands on an insertion point, you choose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.floor()&lt;/code&gt; → the &lt;strong&gt;last value&lt;/strong&gt; that will drive the lambda to search up or stop.
e.g., last &lt;code&gt;i&lt;/code&gt; where &lt;code&gt;nums[i] &amp;lt;= target&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.ceiling()&lt;/code&gt; → the &lt;strong&gt;first value&lt;/strong&gt; that will drive the lambda to search down or stop.
e.g., first &lt;code&gt;i&lt;/code&gt; where &lt;code&gt;nums[i] &amp;gt;= target&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🍌 LeetCode 875: Koko Eating Bananas
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;minSpeed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;piles&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;BinarySearch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forInts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Range&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;piles&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
      &lt;span class="c1"&gt;// if Koko can finish, eat slower, else eat faster&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertionPointFor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;canFinish&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;piles&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;// ceiling() is the first value that canFinish()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ceiling&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧮 LeetCode 69: Integer Square Root
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;mySqrt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;BinarySearch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forInts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Range&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;// if square is larger, try smaller sqrt&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertionPointFor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compare&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;// floor() is the max whose square &amp;lt;= x&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;floor&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📦 LeetCode 410: Split Array Largest Sum
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;splitArray&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAsInt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;BinarySearch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forInts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Range&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;// if canSplit, try smaller sum, else try larger sum&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertionPointFor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxSum&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;canSplit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxSum&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;// ceiling is the min sum that can split&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ceiling&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔍 TL;DR: Logic Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Search Domain&lt;/th&gt;
&lt;th&gt;Predicate&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Eating bananas&lt;/td&gt;
&lt;td&gt;&lt;code&gt;forInts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;canFinish(speed) ? -1 : 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.ceiling()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Square root&lt;/td&gt;
&lt;td&gt;&lt;code&gt;forInts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;compare(x, mid²)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.floor()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;forInts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;canSplit(sum) ? -1 : 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.ceiling()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🤔 Can I Use it on LeetCode?
&lt;/h2&gt;

&lt;p&gt;Not directly. LeetCode doesn’t support third-party libraries.&lt;br&gt;&lt;br&gt;
But you can use it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quickly prototype and verify your binary search algorithm.&lt;/li&gt;
&lt;li&gt;Divide and conquer. Avoid wrestling with bugs and boilerplate until you know your &lt;code&gt;canSplit()&lt;/code&gt;, &lt;code&gt;canFinish()&lt;/code&gt; logic works.&lt;/li&gt;
&lt;/ul&gt;







&lt;p&gt;📦 &lt;a href="https://github.com/google/mug/blob/master/mug-guava/src/test/java/com/google/mu/collect/LeetCodeTest.java" rel="noopener noreferrer"&gt;More LeetCode binary search algorithms&lt;/a&gt;&lt;br&gt;
📦 &lt;a href="https://github.com/google/mug" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>leetcode</category>
      <category>binarysearch</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Iteration - Generate Stream Recursively</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Sun, 23 Jun 2024 23:54:23 +0000</pubDate>
      <link>https://dev.to/fluentfuture/iteration-a-stream-generator-m75</link>
      <guid>https://dev.to/fluentfuture/iteration-a-stream-generator-m75</guid>
      <description>&lt;h2&gt;
  
  
  The Iterators
&lt;/h2&gt;

&lt;p&gt;For anyone having been in programming a couple of years, chances are you've run into an interview question or an assignment like &lt;a href="https://stackoverflow.com/questions/12850889/in-order-iterator-for-binary-tree" rel="noopener noreferrer"&gt;iterator for binary tree&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are variations like for the "pre-order", "post-order" or "in-order" traversal; or sometimes you got a n-ary tree instead of a bnary tree.&lt;/p&gt;

&lt;p&gt;A reason this kind of questions are used in interviews is because they require some meticulous bookkeeping, and there are loads of edge cases to bite you if you aren't careful.&lt;/p&gt;

&lt;p&gt;Fine, it's 2024 and we have &lt;code&gt;Stream&lt;/code&gt; now. How about having some fun and creating an &lt;em&gt;infinite&lt;/em&gt; stream to generate the Fibonacci sequence?&lt;/p&gt;

&lt;p&gt;The one thing in common? They are all &lt;strong&gt;boringly painful&lt;/strong&gt; to write and read. Playing with them for the interview is cool; but having to read them in real life is no fun. &lt;/p&gt;

&lt;h2&gt;
  
  
  Recursion is easy
&lt;/h2&gt;

&lt;p&gt;Imagine you are writing in-order traversal the most naive way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except the hardcoding of &lt;code&gt;System.out.println()&lt;/code&gt;, and never mind the stack overflow caused by the infinite recursion, at least it's easy, right?&lt;/p&gt;

&lt;p&gt;Why can't we have some meat - scratch that - why can't we keep the simplicity and just fix the hardcoding and the stack overflow?&lt;/p&gt;

&lt;h2&gt;
  
  
  Design the API
&lt;/h2&gt;

&lt;p&gt;This is my favorite part: to dream about what I would really really want, if everything just magically works.&lt;/p&gt;

&lt;p&gt;Let's see... I don't want the hardcoding, so maybe this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The imagined &lt;code&gt;generate()&lt;/code&gt; call will put the elements in the final stream.&lt;/p&gt;

&lt;p&gt;It doesn't solve the stack overflow though.&lt;/p&gt;

&lt;p&gt;The very nature of an infinite stream means that it has to be lazy. The caller could decide to use &lt;code&gt;.limit(50)&lt;/code&gt; to only print the first 50 numbers and the recursive call shouldn't progress beyond the first 50 numbers.&lt;/p&gt;

&lt;p&gt;So I need something that delays the recursion call. The idiomatic way in Java to model laziness is to wrap it in a callback interface. So let's create one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Continuation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then I need a method similar to the &lt;code&gt;generate()&lt;/code&gt; call but accepts a &lt;code&gt;Continuation&lt;/code&gt;. The syntax should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I stole the &lt;code&gt;yield&lt;/code&gt; keyword from other languages like C# with native generator support (it means to yield control to the runtime and also yield the evaluation result into the stream). Recent Java versions have made it a reserved word so you'll likely need to call it using &lt;code&gt;this.yield()&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;By wrapping the recursive call in a lambda passed to &lt;code&gt;yield()&lt;/code&gt;, I should get the effect of lazy evaluation. The implementation's job will be to ensure the right order between the directly generated elements and the lazily generated ones.&lt;/p&gt;

&lt;p&gt;In summary, the draft API we have come up with so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Iteration&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Continuation&lt;/span&gt; &lt;span class="n"&gt;continuation&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/** Turns the Iteration into a stream */&lt;/span&gt;
  &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;iterate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case it wasn't obvious, we need the &lt;code&gt;iterate()&lt;/code&gt; method to package it all up as a lazy stream.&lt;/p&gt;

&lt;p&gt;The final syntax we want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InOrderTraversal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Iteration&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;InOrderTraversal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InOrderTraversal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="na"&gt;traverseInOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;iterate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Fibonacci&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Iteration&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Fibonacci&lt;/span&gt; &lt;span class="nf"&gt;startFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;startFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Fibonacci&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;startFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;iterate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Making it
&lt;/h2&gt;

&lt;p&gt;It's nice to have a dream once in a while. We'll be able to use such API to turn many recursive algorithms &lt;em&gt;trivially&lt;/em&gt; into lazy streams. &lt;/p&gt;

&lt;p&gt;But aside from polishing up an API &lt;em&gt;surface&lt;/em&gt; I like, there is nothing in concrete. How do I make it actually work?&lt;/p&gt;

&lt;p&gt;First of all, if the thing only needs to handle &lt;code&gt;generate()&lt;/code&gt;, it'd be a pointlessly trivial wrapper around a &lt;code&gt;Queue&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But we need to handle &lt;code&gt;yield()&lt;/code&gt; with its lazy evaluation. Imagine we are effectively running this sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// line 3&lt;/span&gt;
  &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// line 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At line 3, can we enqueue the Continuation into the same queue before moving on to line 7?&lt;/p&gt;

&lt;p&gt;If I do so, after line 7, the queue will look like &lt;code&gt;[1, 2, Continuation, 5]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now if we call &lt;code&gt;iterate()&lt;/code&gt; and start to consume elements from the stream, &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;2&lt;/code&gt; will be popped out, and then the &lt;code&gt;Continuation&lt;/code&gt; object, with the number &lt;code&gt;5&lt;/code&gt; still remaining in the queue.&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;Continuation&lt;/code&gt; object is consumed, it needs to be evaluated, which will in turn generate &lt;code&gt;3&lt;/code&gt; and &lt;code&gt;4&lt;/code&gt;. The question is where to put the two numbers? &lt;/p&gt;

&lt;p&gt;We can't keep enqueueing them after the number &lt;code&gt;5&lt;/code&gt; because it'll be out of order; we can't treat the queue as a stack and push them in FILO order because then we'll get &lt;code&gt;[4, 3, 5]&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack or Queue?
&lt;/h2&gt;

&lt;p&gt;There are several ways to go about this.&lt;/p&gt;

&lt;p&gt;One possibility is to create a stack of queues, where each time a &lt;code&gt;Continuation&lt;/code&gt; is to be evaluated, push a new queue onto the stack. The stream will always consume elements from the top queue of the stack.&lt;/p&gt;

&lt;p&gt;The downside is that you might end up creating and discarding many instances of &lt;code&gt;ArrayDeque&lt;/code&gt;, which can be wasteful.&lt;/p&gt;

&lt;p&gt;With some micro-optimization in mind, another approach is to use the &lt;a href="https://stackoverflow.com/questions/69192/how-to-implement-a-queue-using-two-stacks" rel="noopener noreferrer"&gt;two-stacks-as-a-queue&lt;/a&gt; trick. &lt;/p&gt;

&lt;p&gt;The are two stacks: the &lt;code&gt;inbox&lt;/code&gt; for writing and &lt;code&gt;outbox&lt;/code&gt; for reading. When either &lt;code&gt;generate()&lt;/code&gt; or &lt;code&gt;yield()&lt;/code&gt; is called, we push into the &lt;br&gt;
&lt;code&gt;inbox&lt;/code&gt; stack; when the stream tries to consume, it flushes everything out of &lt;code&gt;inbox&lt;/code&gt; into the &lt;code&gt;outbox&lt;/code&gt; and then consumes one-by-one from the &lt;code&gt;outbox&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To put it in context, upon seeing &lt;code&gt;[Continuation, 5]&lt;/code&gt; in the &lt;code&gt;outbox&lt;/code&gt;, the &lt;code&gt;Continuation&lt;/code&gt; is evaluated, which puts &lt;code&gt;[4, 3]&lt;/code&gt; in the &lt;code&gt;inbox&lt;/code&gt; stack.&lt;/p&gt;

&lt;p&gt;On the other side, the stream tries to consume. It pops and pushes &lt;code&gt;[4, 3]&lt;/code&gt; onto the &lt;code&gt;outbox&lt;/code&gt; stack, resulting in &lt;code&gt;[3, 4, 5]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Implmentation-wise, we get to allocate no more than two &lt;code&gt;ArrayDeque&lt;/code&gt;s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Iteration&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Deque&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;outbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayDeque&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Deque&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayDeque&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Continuation&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Do not stream Continuation objects"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;inbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Continuation&lt;/span&gt; &lt;span class="n"&gt;continuation&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;inbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;continuation&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;consumeNextOrNull&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(;&lt;/span&gt; &lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;poll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Continuation&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;Continuation&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;evaluate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;poll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// nothing in inbox&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;outbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;poll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// flush inbox -&amp;gt; outbox&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;poll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
         &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
         &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;poll&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;outbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;poll()&lt;/code&gt; private method does the "flush inbox into outbox" logic mentioned above.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;consumeNextOrNull()&lt;/code&gt; method consumes the next element from the two-stack-queue, and evaluates &lt;code&gt;Continuation&lt;/code&gt; when it sees one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap it all up
&lt;/h2&gt;

&lt;p&gt;If you are with me so far, all we are missing is the &lt;code&gt;iterate()&lt;/code&gt; method that wraps it all up as a &lt;code&gt;Stream&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'll cheat by just using Mug's &lt;a href="https://google.github.io/mug/apidocs/com/google/mu/util/stream/MoreStreams.html#whileNotNull(java.util.function.Supplier)" rel="noopener noreferrer"&gt;&lt;code&gt;whileNotNull()&lt;/code&gt;&lt;/a&gt; convenience method. But it's not hard to create your own by implementing a &lt;code&gt;Spliterator&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;iterate&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;MoreStreams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;whileNotNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;consumeNextOrNull&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with that, our little generic recursive stream generator is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use it for real
&lt;/h2&gt;

&lt;p&gt;Before we call it the day, let's see if we can use it to solve a more realistic problem. &lt;/p&gt;

&lt;p&gt;Say, you are calling a &lt;code&gt;ListAssets&lt;/code&gt; API that supports pagination. Request and response definitions are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ListAssetsRequest {
  string userId;
  int page_size;
  string page_token;  // start from this page
}

ListAssetsResponse {
  List&amp;lt;Asset&amp;gt; assets;
  string next_page_token;  // empty if no more
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we were to naively fetch all pages and print them, it'll be as simple as sending request over and over again until the &lt;code&gt;response.next_page_token&lt;/code&gt; is empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showAllAssets&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;ListAssetsRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nc"&gt;ListAssetsRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setUserId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(;&lt;/span&gt; &lt;span class="o"&gt;;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assetsApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listAssets&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Asset&lt;/span&gt; &lt;span class="n"&gt;asset&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAssets&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asset&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNextPageToken&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// no more pages&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPageToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNextPageToken&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we can do better! Let's wrap it up as a lazy &lt;code&gt;Stream&amp;lt;Asset&amp;gt;&lt;/code&gt; to give callers more flexibility. For example they can consume any number of assets and stop when needed without over-fetching pages unnecessarily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Asset&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;listAssets&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pagination&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Iteration&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ListAssetsResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Pagination&lt;/span&gt; &lt;span class="nf"&gt;startFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;pageToken&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assetsApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listAssets&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;ListAssetsRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUserId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPageSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPageToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageToken&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
      &lt;span class="n"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNextPageToken&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;startFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNextPageToken&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Pagination&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;startFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;iterate&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAssets&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I trust you can read it alright, my friend. :)&lt;/p&gt;

</description>
      <category>java</category>
      <category>stream</category>
      <category>recursive</category>
      <category>functional</category>
    </item>
    <item>
      <title>Chain - a Goofy, Immutable List to Concatenate Millions</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Wed, 12 Jun 2024 05:04:14 +0000</pubDate>
      <link>https://dev.to/fluentfuture/chain-a-goofy-functional-tree-backed-list-34dm</link>
      <guid>https://dev.to/fluentfuture/chain-a-goofy-functional-tree-backed-list-34dm</guid>
      <description>&lt;h2&gt;
  
  
  What?
&lt;/h2&gt;

&lt;p&gt;Java has array-based &lt;code&gt;List&lt;/code&gt;s for efficient random access; there are &lt;code&gt;LinkedList&lt;/code&gt; for efficient appending.&lt;/p&gt;

&lt;p&gt;But there is none that you can concatenate a million times for cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Once upon a time
&lt;/h2&gt;

&lt;p&gt;Agent Aragorn (Son of Arathorn), Jonny English and Smith collaborated on a bunch of missions.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Mission&lt;/code&gt; class's signature is like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Mission&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nc"&gt;MissionId&lt;/span&gt; &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nc"&gt;Range&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;timeWindow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nc"&gt;ImmutableSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to create an &lt;code&gt;ImmutableRangeMap&amp;lt;LocalDate, Set&amp;lt;Agent&amp;gt;&amp;gt;&lt;/code&gt; (&lt;code&gt;RangeMap&lt;/code&gt; is a Guava collection that maps disjoint ranges to values) to account for all the agents during each time window. Note that missions can have overlapping time windows, and agents could work on multiple missions at the same time.&lt;/p&gt;

&lt;p&gt;So for missions like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;missions = [{
  timeWindow: [10/01..10/30]
  agents: [Aragorn, English]
}, {
  timeWindow: [10/15..11/15]
  agents: [Aragorn, Smith]
}]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I want the result to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[10/01..10/15): [Aragorn, English]
[10/15..10/30]: [Aragorn, English, Smith]
(10/30..11/15]: [Aragorn, Smith] 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first I thought to use the &lt;a href="https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/ImmutableRangeMap.html#toImmutableRangeMap(java.util.function.Function,java.util.function.Function)" rel="noopener noreferrer"&gt;toImmutableRangeMap()&lt;/a&gt; collector, as in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;missions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toImmutableRangeMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Mission:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;timeWindow&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;Mission:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila, done, right?&lt;/p&gt;

&lt;p&gt;Not quite. My colleague pointed out that &lt;code&gt;toImmutableRangeMap()&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; allow overlapping ranges. It wants all input time windows to be disjoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;RangeMap&lt;/code&gt; can &lt;code&gt;merge()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;TreeRangeMap&lt;/code&gt; class has a &lt;a href="https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/TreeRangeMap.html#merge(com.google.common.collect.Range,V,java.util.function.BiFunction)" rel="noopener noreferrer"&gt;merge()&lt;/a&gt; method that already does the heavylifting: finds overlappings and splits the ranges, and then merges the values mapped to the overlapping subrange. &lt;/p&gt;

&lt;p&gt;With some effort, I created a &lt;a href="https://google.github.io/mug/apidocs/com/google/mu/util/stream/GuavaCollectors.html#toImmutableRangeMap(java.util.function.BinaryOperator)" rel="noopener noreferrer"&gt;toImmutableRangeMap(merger)&lt;/a&gt; &lt;code&gt;BiCollector&lt;/code&gt; on top of the &lt;code&gt;merge()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;So if what I needed is just to count the number of agents, I could have done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;google&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BiStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;biStream&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;ImmutableRangeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;agentCounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;biStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapKeys&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Mission:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;timeWindow&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapValues&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mission&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mission&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toImmutableRangeMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Integer:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(It'll double count the duplicate agents though)&lt;/p&gt;

&lt;p&gt;Anyhoo, here goes the interesting part: &lt;strong&gt;&lt;em&gt;how do I merge the &lt;code&gt;Set&amp;lt;Agent&amp;gt;&lt;/code&gt;?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Quadratic runtime
&lt;/h2&gt;

&lt;p&gt;I could use Guava's &lt;code&gt;Sets.union()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.common.collect.Sets&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;ImmutableRangeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ImmutableSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;agentsTimeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;biStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapKeys&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Mission:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;timeWindow&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapValues&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mission&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mission&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toImmutableRangeMap&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;set1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Sets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;union&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set2&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;immutableCopy&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gotcha is that each time merging happens, merging two original sets into one is &lt;code&gt;O(n)&lt;/code&gt; where n is the number of agents from the two overlapping ranges. If we are unlucky, we can get into the situation where a time window is repetitively discovered to overlap with another time window, and we keep copying and copying over again. The time complexity is quadratic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack overflow
&lt;/h2&gt;

&lt;p&gt;Could I remove the &lt;code&gt;.immutableCopy()&lt;/code&gt;? &lt;code&gt;Sets.union()&lt;/code&gt; returns a view that takes constant time so we should be good?&lt;/p&gt;

&lt;p&gt;Not really. We don't know how many times merging will happen, a &lt;code&gt;Set&lt;/code&gt; can be unioned, then unioned again for unknown times. In the worst case, we'd create a union-of-union-of-union N levels deep. If N is a large number, we'll stack overflow when we try to access the final &lt;code&gt;SetView&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;The same will happen if for example I use &lt;code&gt;Iterables.concat()&lt;/code&gt; or &lt;code&gt;Stream.concat()&lt;/code&gt; (&lt;a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/Stream.html#concat(java.util.stream.Stream,java.util.stream.Stream)" rel="noopener noreferrer"&gt;javadoc&lt;/a&gt; discusses this problem).&lt;/p&gt;

&lt;p&gt;And in case it wasn't obvious, the merging cannot modify either of the two lists or sets, because they are still associated with the sub-range that doesn't overlap. So we need it to be immutable.&lt;/p&gt;

&lt;p&gt;If I had one of the &lt;a href="https://en.wikipedia.org/wiki/Persistent_data_structure" rel="noopener noreferrer"&gt;persistent collections&lt;/a&gt; in the dependencies, I might just use it (they offer O(logn) performance for concatenation but usually are close to constant time). But I don't. And it doesn't feel like worth it to pull in one of such libraries for a single use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put it in a &lt;em&gt;tree&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;I slept on this problem for about two days for an idea to come to me: can we use something like Haskell's &lt;code&gt;List&lt;/code&gt;? &lt;/p&gt;

&lt;p&gt;Tl;dr, Haskell's List is like &lt;code&gt;LinkedList&lt;/code&gt; except it's immutable. So given a list of &lt;code&gt;[2, 3]&lt;/code&gt;, you can &lt;code&gt;cons&lt;/code&gt; the number 1 onto the list to get a new instance of &lt;code&gt;[1, 2, 3]&lt;/code&gt;. Under the hood it's as simple as creating a new object with the internal &lt;code&gt;tail&lt;/code&gt; pointer pointing to the old &lt;code&gt;[2, 3]&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;If I can do this, each time merging happens, I only need to pay O(1) cost. The resulting object is probably less efficient for random access than &lt;code&gt;ArrayList&lt;/code&gt; or Guava's &lt;code&gt;ImmutableList&lt;/code&gt; because of all the pointers and indirections. But that's okay. When the whole split-merge process is done, I can perform a final copy into &lt;code&gt;ImmutableList&lt;/code&gt;, which is O(n).&lt;/p&gt;

&lt;p&gt;The only problem? Haskell's &lt;code&gt;cons&lt;/code&gt; only allows to add one element, while I have two &lt;code&gt;List&amp;lt;Agent&amp;gt;&lt;/code&gt;s to concatenate (I can't &lt;code&gt;cons&lt;/code&gt; every element from one of the lists, because then I'm getting back to quadratic).&lt;/p&gt;

&lt;p&gt;To support &lt;code&gt;concat(list1, list2)&lt;/code&gt;, I decided to use a binary tree to represent the List's state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nd"&gt;@Nullable&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// null means empty&lt;/span&gt;
  &lt;span class="nd"&gt;@Nullable&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// null means empty&lt;/span&gt;

  &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{...}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the list, the elements in &lt;code&gt;left&lt;/code&gt; show up first, followed by &lt;code&gt;mid&lt;/code&gt;, then followed by the elements in &lt;code&gt;right&lt;/code&gt;. In other words, an in-order traversal will give us back the list.&lt;/p&gt;

&lt;p&gt;The key trick is to figure out how to concatenate two binary trees into one. Intuitively, I need to find the new "mid point" value, which can be either the &lt;code&gt;left&lt;/code&gt; tree's last element, or the &lt;code&gt;right&lt;/code&gt; tree's first element. Say, if I take the &lt;code&gt;right&lt;/code&gt; tree's first element, then the new tree's &lt;code&gt;left&lt;/code&gt; remains the old &lt;code&gt;left&lt;/code&gt;, while the new tree's &lt;code&gt;right&lt;/code&gt; would need to be the old &lt;code&gt;right&lt;/code&gt; after &lt;strong&gt;removing the first element&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap it up
&lt;/h2&gt;

&lt;p&gt;Since the Tree is immutable, how do I &lt;em&gt;remove&lt;/em&gt; any element at all? And in a binary tree, finding the first element takes up to O(n) time (it's not a balanced tree).&lt;/p&gt;

&lt;p&gt;It turns out there's a &lt;a href="https://en.wikipedia.org/wiki/Indirection#:~:text=A%20famous%20aphorism%20of%20Butler,for%20%22level%20of%20indirection%22." rel="noopener noreferrer"&gt;law&lt;/a&gt; in computer science:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All problems in computer science can be solved by another level of indirection&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In human language: if a problem can't be solved with one layer of indirection, add two :)&lt;/p&gt;

&lt;p&gt;Here goes my second layer of indirection that handles the &lt;em&gt;remove first element from an immutable list&lt;/em&gt; task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nd"&gt;@Nullable&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;newHead&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;head&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;newTail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tail&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;head&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tail&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;newHead&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newTail&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is quite like Haskell's &lt;code&gt;cons&lt;/code&gt; list, except the &lt;code&gt;tail&lt;/code&gt; is a binary tree instead of another &lt;code&gt;cons&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;Now because both left and right &lt;code&gt;Chain&lt;/code&gt; already have the first element readily accessible, I can just take the right &lt;code&gt;head&lt;/code&gt; as the new mid point to build the new tree, with the tail from left and the tail from right. This new Tree maintains the order invariant as in &lt;code&gt;left.tail -&amp;gt; right.head -&amp;gt; right.tail&lt;/code&gt;. And of course the left's head becomes the new &lt;code&gt;Chain&lt;/code&gt; head.&lt;/p&gt;

&lt;p&gt;It takes a bit of brain gymnastics. But if you sit down and think for a minute, it's actually pretty straight forward.&lt;/p&gt;

&lt;p&gt;This solves the O(1) concatenation. And the good thing is that, no matter how deep &lt;code&gt;concat()&lt;/code&gt; is nested, the result is always one layer of &lt;code&gt;Chain&lt;/code&gt; with a heap-allocated &lt;code&gt;Tree&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Now we just need to make sure when we iterate through the &lt;code&gt;Chain&lt;/code&gt;, we take no more than O(n) time, and constant stack space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flattening tree back to &lt;code&gt;List&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;My secret weapon is &lt;a href="https://google.github.io/mug/apidocs/com/google/mu/util/graph/Walker.html#inBinaryTree(java.util.function.UnaryOperator,java.util.function.UnaryOperator)" rel="noopener noreferrer"&gt;Walker.inBinaryTree()&lt;/a&gt; (but you can create your own - it's standard tree traversal stuff). It already does everything I needed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O(n) time &lt;em&gt;lazy&lt;/em&gt; in-order traversal.&lt;/li&gt;
&lt;li&gt;Constant stack space.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using it is pretty simple. First we add a &lt;code&gt;stream()&lt;/code&gt; method to the &lt;code&gt;Tree&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Walker&lt;/span&gt;&lt;span class="o"&gt;.&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;inBinaryTree&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inOrderFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mid&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://google.github.io/mug/apidocs/com/google/mu/util/graph/BinaryTreeWalker.html#inOrderFrom(java.lang.Iterable)" rel="noopener noreferrer"&gt;&lt;code&gt;inOrderFrom()&lt;/code&gt;&lt;/a&gt; method returns a lazy stream, which will take at the worst case O(n) heap space and constant stack space.&lt;/p&gt;

&lt;p&gt;Then we wrap and polish it up in our wrapper &lt;code&gt;Chain&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chain&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Returns a &amp;lt;em&amp;gt;lazy&amp;lt;/em&amp;gt; stream of the elements in this list.
   * The returned stream is lazy in that concatenated chains aren't consumed until the stream
   * reaches their elements.
   */&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
        &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;concat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, it gives me O(n) time read access to the tree and I can easily &lt;code&gt;collect()&lt;/code&gt; it into an &lt;code&gt;ImmutableList&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://github.com/google/mug/blob/master/mug/src/main/java/com/google/mu/collect/Chain.java" rel="noopener noreferrer"&gt;actual implementation&lt;/a&gt;, I also made &lt;code&gt;Chain implements List&lt;/code&gt; to make it nicer to use, and used lazy initialization to pay the cost only once. But that's just some extra API makeup. The meat is all here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Concatenating Strings?
&lt;/h2&gt;

&lt;p&gt;If we didn't have an agent list, but a string, how would you go about concatenating the strings without quadratic cost?&lt;/p&gt;

&lt;p&gt;You could adopt a similar tree technique. But you can probably just reuse &lt;code&gt;Chain&lt;/code&gt;. For example, wrap your leaf-level strings as &lt;code&gt;Chain&amp;lt;String&amp;gt;&lt;/code&gt; using &lt;code&gt;Chain.of(string)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then just keep concatenating the &lt;code&gt;Chain&amp;lt;String&amp;gt;&lt;/code&gt; until at the very end, you can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;finalConcatenatedString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;finalStringChain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joining&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;// O(n)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Chain&lt;/code&gt; is a simple &lt;em&gt;immutable&lt;/em&gt; &lt;code&gt;List&lt;/code&gt; implementation that you can append, concatenate millions of times.&lt;/p&gt;

&lt;p&gt;A bit of googling shows that people have run into &lt;a href="https://www.google.com/search?q=java+merge+lists+in+constant+time&amp;amp;oq=java+merge&amp;amp;gs_lcrp=EgZjaHJvbWUqCAgAEEUYJxg7MggIABBFGCcYOzIMCAEQABhDGIAEGIoFMgwIAhAAGEMYgAQYigUyBwgDEAAYgAQyBggEEEUYPDIGCAUQRRg8MgYIBhBFGDwyBggHEEUYQNIBCDIxNjVqMGo3qAIAsAIA&amp;amp;sourceid=chrome&amp;amp;ie=UTF-8" rel="noopener noreferrer"&gt;similar needs&lt;/a&gt; but I didn't find a similar implementation that handles both the O(1) concatenation time and stack overflow concern.&lt;/p&gt;

</description>
      <category>java</category>
      <category>functional</category>
      <category>immutable</category>
      <category>concat</category>
    </item>
    <item>
      <title>Parsing dates and time without pattern string in Java</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Mon, 29 Apr 2024 02:23:39 +0000</pubDate>
      <link>https://dev.to/fluentfuture/parsing-dates-and-time-in-java-3c37</link>
      <guid>https://dev.to/fluentfuture/parsing-dates-and-time-in-java-3c37</guid>
      <description>&lt;p&gt;The other day I needed to parse some timestamp strings loaded from a file. It's for a test where I wanted to feed these timestamps as &lt;code&gt;Instant&lt;/code&gt; to an object I'm trying test. These time strings are like &lt;code&gt;2023-12-05 17:51:00-08:00&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;First thing I tried was to just parse it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeString&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course it throws, right? Looked like I had to roll up my sleeves and figure out the right &lt;code&gt;DateTimeFormatter&lt;/code&gt; before being able to parse them.&lt;/p&gt;

&lt;p&gt;I kinda remembered that for the date, it's &lt;code&gt;yyyy-MM-dd&lt;/code&gt; (important to use the upper case &lt;code&gt;MM&lt;/code&gt;); and for the time it's &lt;code&gt;hh-mm-ss&lt;/code&gt;. But I had no idea how to specify the timezone offset part.&lt;/p&gt;

&lt;p&gt;So I went to read the javadoc of &lt;code&gt;DateTimeFormatter&lt;/code&gt;. In retrospect, if I were in the mood of learning the API for the purpose of learning, it must have been fine. But I was more like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;C'mon, I just need to parse some timestamp. Where is the quick example that matches the format?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unfortunately the javadoc reads more like a RFC document, with no cheetsheet that could help me in a hurry.&lt;/p&gt;

&lt;p&gt;After deciding to sit down, and take my time to learn it, I finally figured out. And by the way I was wrong, it's not &lt;code&gt;hh-mm-ss&lt;/code&gt; but &lt;code&gt;HH-mm-ss&lt;/code&gt;. Oh well.&lt;/p&gt;

&lt;p&gt;End of story.&lt;/p&gt;

&lt;p&gt;But I hated this kind of yak shaving. You need to put aside the immediate task you had in mind, and instead research the knowledge for a preliminary step. &lt;/p&gt;

&lt;p&gt;A colleague told me that what I want is something &lt;a href="https://go.dev/src/time/format.go" rel="noopener noreferrer"&gt;Golang does&lt;/a&gt;. That is, they don't use cryptic format specifiers, but instead just use an example datetime string.&lt;/p&gt;

&lt;p&gt;I was like "shut up and take my money!" because it's exactly what I need: I don't have the specifiers, but I do have an example date time string.&lt;/p&gt;

&lt;p&gt;Learning a bit more though, Golang's use of the magical reference date gave me the pause: if I can't remember the format specifiers, can I remember the magic date? It turns out that reference time isn't really intuitive either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;01/02 03:04:05PM 06 -0700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That stikes me as a bit too cute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What year is &lt;code&gt;01/02&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Why PM? While it looks like natural &lt;code&gt;1-2-3-4-5-6-7&lt;/code&gt;, in the time format I'm most familiar with, it'll actually be &lt;code&gt;15:04:05&lt;/code&gt;, not easy to remember at all.&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;-0700&lt;/code&gt; but not &lt;code&gt;+07:00&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if it only takes the reference date, I can't just pass a random string to parse without myself having to translate it to the reference date with my own man power.&lt;/p&gt;

&lt;p&gt;The burning question is: &lt;em&gt;if I can read &lt;code&gt;2023-12-05 17:51:00-08:00&lt;/code&gt; and understand exactly what time it is, no ambiguity, and without knowing a reference time or the format specifiers, why can't the computer?&lt;/em&gt; This is no rocket science or AlphaGo.&lt;/p&gt;

&lt;p&gt;So I started to build &lt;a href="https://github.com/google/mug/wiki/Parsing-Date-Time-Should-Be-10x-Easier" rel="noopener noreferrer"&gt;a library&lt;/a&gt; to help me parse all common dates and time strings. It should be able to infer from the input what the format specifiers should be (supports timezone, weekday, even including formats like &lt;code&gt;11/30/2024&lt;/code&gt;), and then use that format specifier to parse the date or time for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Directly parsing dates and time&lt;/span&gt;
&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DateTimeFormats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseToInstant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
   &lt;span class="s"&gt;"2023-12-05 17:51:00-08:00"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DateTimeFormats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseLocalDate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
   &lt;span class="s"&gt;"2023-12-05"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;ZonedDateTime&lt;/span&gt; &lt;span class="n"&gt;zonedDateTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DateTimeFormats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseZonedDateTime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
   &lt;span class="s"&gt;"2023-12-05 17:51:00-08:00"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Infer the DateTimeFormatter&lt;/span&gt;
&lt;span class="nc"&gt;DateTimeFormatter&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DateTimeFormats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
   &lt;span class="s"&gt;"2023-12-05 17:51:00-08:00"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was a fun little project to build. Using the &lt;a href="https://github.com/google/mug/wiki/Substring-Explained" rel="noopener noreferrer"&gt;Substring&lt;/a&gt; library as the basic building block, I mostly avoided having to use regex or fiddling with string indexes and off-by-1 errors. &lt;/p&gt;

&lt;p&gt;I like that it allows me to use any example date and time, without having to remember anything at all.&lt;/p&gt;

&lt;p&gt;Maybe it'll be useful to you too?&lt;/p&gt;

</description>
      <category>java</category>
      <category>datetime</category>
      <category>datetimeformat</category>
      <category>gotime</category>
    </item>
    <item>
      <title>String Manipulation Tricks (Day 1)</title>
      <dc:creator>Dash One</dc:creator>
      <pubDate>Fri, 19 Apr 2024 01:42:33 +0000</pubDate>
      <link>https://dev.to/fluentfuture/string-manipulation-tricks-day-1-2ep1</link>
      <guid>https://dev.to/fluentfuture/string-manipulation-tricks-day-1-2ep1</guid>
      <description>&lt;p&gt;(First post. Don't know what to blog about. I'll start by recording some random coding thought whenever they occur to me and I happen to have time to write it down)&lt;/p&gt;

&lt;p&gt;I've recently been thinking about Java string manipulation. Then I stumbled upon this string parsing code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="no"&gt;THE_NUMBER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"^\\s\\(([1-9][0-9]*)\\)$"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Find the number from string with header.&lt;/span&gt;
&lt;span class="c1"&gt;// example: parseTheNumber("foo", "foo (123)") -&amp;gt; 123&lt;/span&gt;
&lt;span class="c1"&gt;//          parseTheNumber("foo", "bar (123)") -&amp;gt; empty&lt;/span&gt;
&lt;span class="c1"&gt;//          parseTheNumber("foo", "foo (str)") -&amp;gt; empty&lt;/span&gt;
&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parseTheNumber&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;Matcher&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;THE_NUMBER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
        &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NumberFormatException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a &lt;a href="http://github.com/google/mug/wiki/Just-Say-No-to-Regex"&gt;racist against Java Regex&lt;/a&gt;, I can't help wanting to try it out without regex (using &lt;a href="http://github.com/google/guava"&gt;Guava&lt;/a&gt; and &lt;a href="http://github.com/google/mug"&gt;Mug&lt;/a&gt;, my all-in-one toolbox).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.common.primitives.Ints&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.mu.util.StringFormat&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;StringFormat&lt;/span&gt; &lt;span class="no"&gt;THE_NUMBER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{type} ({num})"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parseTheNumber&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// StringFormat.parse() will return empty()&lt;/span&gt;
  &lt;span class="c1"&gt;//     if failed to parse, or if the lambda returns null&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;THE_NUMBER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Ints.tryParse() returns null if failed to parse&lt;/span&gt;
      &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Ints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryParse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="http://github.com/google/mug/wiki/StringFormat-Explained"&gt;&lt;code&gt;StringFormat&lt;/code&gt;&lt;/a&gt; performs well compared to Java regex. And to me it's more readable.&lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

</description>
      <category>java</category>
    </item>
  </channel>
</rss>
