Java 21 API Additions: StringBuilder.repeat, Math.clamp

Introduction Alongside virtual threads and pattern matching, Java 21Click to read tutorial: Overview of New Features in Java 21 added a set of small methods to the core libraries which make everyday code shorter. This tutorial explains the Java 21 API additions with examples - StringBuilder.repeat(), String.indexOf() with a range and splitWithDelimiters(), Math.clamp(), Character methods for emoji, an HttpClient which can be closed, and the new Key Encapsulation Mechanism API. Every example is compiled for Java 21. StringBuilder.repeat() and new String methods
StringBuilder.repeat, indexOf with a range, splitWithDelimiters
//StringAdditions.java
import java.util.Arrays;

public class StringAdditions {
  public static void main(String[] args) {
    StringBuilder sb = new StringBuilder();
    sb.append('+').repeat("-", 10).append('+').append('\n');    // repeat a CharSequence
    sb.append('|').repeat(' ', 10).append("|\n");               // repeat a character (code point)
    sb.append('+').repeat("-", 10).append('+');
    System.out.println(sb);

    String path = "/usr/local/lib/java";
    int slash = path.indexOf('/', 1, 10);                       // search only between index 1 and 10
    System.out.println("First '/' between 1 and 10: " + slash);

    String expr = "12+3*4-5";
    System.out.println(Arrays.toString(expr.split("[+*-]")));                 // delimiters lost
    System.out.println(Arrays.toString(expr.splitWithDelimiters("[+*-]", 0))); // delimiters kept
  }
}
 OUTPUT of the above code
+----------+
|          |
+----------+
First '/' between 1 and 10: 4
[12, 3, 4, 5]
[12, +, 3, *, 4, -, 5]
  • StringBuilder.repeat() (and the same method on StringBuffer) appends a string or a character several times, complementing String.repeat() from Java 11, without creating an intermediate string.
  • indexOf(ch, beginIndex, endIndex) searches only within a range - here it finds the second slash, at index 4, and would ignore any after index 10.
  • splitWithDelimiters() splits like split() but keeps the matched delimiters as separate elements, which is exactly what a simple tokenizer for an arithmetic expression needs.
Math.clamp() Keeping a value within a range is a very common operation - a percentage between 0 and 100, a page number between 1 and the last page. It used to be written as Math.min(Math.max(value, min), max), which is easy to get backwards. Java 21 added Math.clamp() -
Clamping values to a range
//ClampDemo.java
public class ClampDemo {
  public static void main(String[] args) {
    for (int value : new int[] {-20, 45, 130}) {
      System.out.println(value + " -> " + Math.clamp(value, 0, 100));
    }
    long bigPage = 5_000_000_000L;
    int page = Math.clamp(bigPage, 1, 250);            // clamp a long into an int range: returns int
    System.out.println("Page: " + page);
    System.out.println("Opacity: " + Math.clamp(1.7, 0.0, 1.0));
    try {
      Math.clamp(5, 10, 1);                           // min greater than max
    } catch (IllegalArgumentException e) {
      System.out.println("Bad range: " + e.getMessage());
    }
  }
}
 OUTPUT of the above code
-20 -> 0
45 -> 45
130 -> 100
Page: 250
Opacity: 1.0
Bad range: 10 > 1
There are versions for int, long, float and double. The version taking a long value with int bounds returns an int, so it doubles as a safe narrowing conversion. Emoji in Character Java 21 added methods to Character which test the Unicode emoji properties of a code point, such as isEmoji(), isEmojiPresentation() and isExtendedPictographic() - useful for counting, filtering or validating user text -
Finding emoji in text
//EmojiDemo.java
public class EmojiDemo {
  public static void main(String[] args) {
    String message = "Java 21 is here 🚀 enjoy ☕";   // rocket and hot beverage
    long emojiCount = message.codePoints().filter(Character::isEmojiPresentation).count();
    System.out.println("Emoji found: " + emojiCount);
    message.codePoints().filter(Character::isEmojiPresentation)
        .forEach(cp -> System.out.println("  U+" + Integer.toHexString(cp).toUpperCase()));
    System.out.println("Is the digit '2' an emoji? " + Character.isEmoji('2')
        + ", shown as emoji by default? " + Character.isEmojiPresentation('2'));
  }
}
 OUTPUT of the above code
Emoji found: 2
  U+1F680
  U+2615
Is the digit '2' an emoji? true, shown as emoji by default? false
Note the difference: digits are technically emoji characters (they combine into keycap emoji), so isEmoji('2') is true, but they are not displayed as emoji by default, which isEmojiPresentation() reports.
A closeable HttpClient and the KEM API The Java 11 HTTP ClientClick to read: Java 11 HTTP Client now implements AutoCloseable, so its threads and connections can be released explicitly, in a try-with-resources block. And Java 21 (JEP 452) added javax.crypto.KEM, an API for key encapsulation mechanisms: a sender uses the receiver's public key to generate a fresh secret key plus an "encapsulation" of it, and only the receiver's private key can recover the same secret key from the encapsulation. KEMs are the building block of modern protocols such as TLS 1.3 and HPKE, and of the quantum-resistant ML-KEMClick to read: quantum-resistant ML-KEM added in Java 24 -
Sharing a secret key with DHKEM
//KemDemo.java
import java.net.http.HttpClient;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.Arrays;
import javax.crypto.KEM;
import javax.crypto.SecretKey;

public class KemDemo {
  public static void main(String[] args) throws Exception {
    try (HttpClient client = HttpClient.newHttpClient()) {        // closed automatically (Java 21)
      System.out.println("HttpClient created and closed");
    }

    KeyPair receiver = KeyPairGenerator.getInstance("X25519").generateKeyPair();
    KEM kem = KEM.getInstance("DHKEM");

    KEM.Encapsulator sender = kem.newEncapsulator(receiver.getPublic());
    KEM.Encapsulated result = sender.encapsulate();                // a new secret + its encapsulation
    SecretKey senderKey = result.key();
    byte[] encapsulation = result.encapsulation();                 // sent to the receiver

    KEM.Decapsulator decapsulator = kem.newDecapsulator(receiver.getPrivate());
    SecretKey receiverKey = decapsulator.decapsulate(encapsulation);

    System.out.println("Encapsulation size: " + encapsulation.length + " bytes");
    System.out.println("Both sides have the same key: " + Arrays.equals(senderKey.getEncoded(), receiverKey.getEncoded()));
  }
}
 OUTPUT of the above code
HttpClient created and closed
Encapsulation size: 32 bytes
Both sides have the same key: true
The shared secret key is then used with a symmetric cipher such as AES-GCM to encrypt the actual data. Other additions Java 21 also added Collections.shuffle(list, RandomGenerator), accepting any of the Java 17 random generatorsClick to read: Java 17 random generators, and the sequenced collectionsClick to read tutorial: Java 21 Sequenced Collections Explained methods. Summary In this tutorial we went through the library additions in Java 21. We built text with StringBuilder.repeat(), searched a range with indexOf(), tokenized with splitWithDelimiters(), kept values in range with Math.clamp(), and found emoji with the new Character methods. Lastly, we closed an HttpClient with try-with-resources, and shared a secret key with the new KEM API. To see everything new from Java 18 to 21 in one place, read the overview of new features in Java 21Click to read tutorial: Overview of New Features in Java 21.