Home | Send Feedback | Share on Bluesky |

Small changes in Java 18 to 21

Published: 12. May 2020  •  Updated: 31. August 2025  •  java

In this blog post we take a look at the smaller changes in Java 18 to 21. A smaller change is, for example, a new method added to an existing class or smaller language changes. I don't cover in this article major changes like a new language feature or a major new package like the new HTTP client or the Class File API.

I also focus on useful features for my daily programming life as an application developer. I'm personally less interested in changes in low-level APIs like Reflection and low-level IO. I also omit in this blog post changes to the Java Virtual Machine (JVM), like new Garbage Collectors (GC).

Here are the other parts of this series:

Java 18 (March 2022)

This release changes the default charset encoding of the platform to UTF-8 (JEP 400). It also brings a simple webserver to the core library (JEP 408). It is also now easier to add code snippets to the Javadoc (JEP 413).

In Java 18 java.lang.reflect.Method, Constructor, and Field were reimplemented on top of java.lang.invoke method handles (JEP 416), and lastly, hostname and address resolution got a service-provider interface (SPI) so that java.net.InetAddress can use resolvers other than the platform's built-in resolver.

New Math and StrictMath methods

This release adds a bunch of new methods to the Math and StrictMath class.

Java 19 (September 2022)

Apart from a lot of Incubator and Preview features, this release introduces a port for Linux/RISC-V (JEP 422).

Factory methods for creating Map and Set

Java 19 introduces new factory methods for HashMap, HashSet, LinkedHashMap, LinkedHashSet, and WeakHashMap.

These methods create a new empty Map or Set for the expected number of mappings or elements. The initial capacity is generally large enough so that the expected number of mappings or elements can be added without resizing the map or set.

parallelMultiply

The BigInteger gets a new parallelMultiply(BigInteger val) to multiply numbers.

Compared to multiply(BigInteger val), parallelMultiply uses a parallel multiplication algorithm that typically uses more CPU resources and memory to compute the result faster. Note that an application might only benefit of the parallel algorithm when both numbers are large, typically in the thousands of bits. This method returns the same mathematical result as multiply(BigInteger val).

Java 20 (March 2023)

Like Java 19, this release brings a lot of Incubator and Preview features. Java 20 now supports Unicode 15.

Regarding minor changes, there are new methods for named groups in regular expressions.

Java 21 (September 2023)

The most significant addition in this release is the support for Virtual Threads (JEP 444). Java 21 adds two new enhancements to the Java language. Deconstruction of record values with Record patterns (JEP 440) and Pattern Matching for switch (JEP 441) introduces pattern matching in switch expressions and statements.

The Z Garbage Collector (ZGC) got an update with (JEP 439).

The Windows 32-bit x86 port of Java is now deprecated and might be removed in a future release (JEP 449). Java 21 now warns when agents are loaded dynamically into a running JVM as a preparation for a future release that disallows dynamic loading of agents by default (JEP 451).

JEP 452 Introduces the Key Encapsulation Mechanism API, an encryption technique for securing symmetric keys using public key cryptography.

Sequenced Collections JEP 431

Java 21 introduces three new interfaces to the collections framework.

Collections that implement these interfaces store their elements in a defined encounter order. Each such collection has a well-defined first, second, and so forth element.

The Javadoc for each new interface shows which existing collections implement the new interfaces. The most obvious example of a sequenced collection is ArrayList, which stores the elements in an array. But also, LinkedHashMap and LinkedHashSet store their elements in a defined encounter order.

The SequencedCollection<E> interface defines the following methods:

reversed() returns a reverse-ordered view of the collection.

SequencedSet<E> is a sub-interface of SequencedCollection<E> and adds the definition of the following method: SequencedSet<E> reversed()

Note that TreeSet implements the SequencedSet<E> interface, but the addFirst and addLast methods throw UnsupportedOperationException. The comparison method determines the encounter order of elements; therefore, explicit positioning is not supported.

Sequenced maps do not implement SequencedCollection<E> instead they implement SequencedMap<K,V>, which defines the following methods:

TreeMap implements SequencedCollection<E>, but the putFirst and putLast methods throw UnsupportedOperationException. The comparison method determines the encounter order of mappings; therefore, explicit positioning is not supported.

  List<String> abc = new ArrayList<>();
  abc.add("b");
  abc.add("c");
  abc.addFirst("a");
  abc.addLast("d");

  System.out.println(abc.getFirst()); // "a"
  System.out.println(abc.getLast()); // "d"

  
  abc.removeFirst();
  abc.removeLast();
  System.out.println(abc.getFirst()); // "b"
  System.out.println(abc.getLast()); // "c"

  for (String s : abc.reversed()) {
      System.out.println(s);
  }
  // c
  // d
  var map = new LinkedHashMap<>();
  map.put(1, "one");
  map.put(2, "two");
  map.putFirst(0, "zero");
  map.putLast(3, "three");

  System.out.println(map.firstEntry()); // 0=zero
  System.out.println(map.lastEntry());  // 3=three

  for (var entry : map.reversed().entrySet()) {
      System.out.println(entry);
  }
  // 3=three
  // 2=two
  // 1=one
  // 0=zero

  var first = map.pollFirstEntry();
  System.out.println(first); // 0=zero

  var last = map.pollLastEntry();
  System.out.println(last);  // 3=three

  System.out.println(map);   // {1=one, 2=two}

clamp

The Math and StrictMath classes got new clamp() methods to conveniently clamp the numeric value between the specified minimum and maximum values. Each class got four overloads of these methods to support int, long, float, and double.

  int i = 12;
  double d = -12.0;
  float f = 23.5f;
  long l = 1234567890L;

  System.out.println(Math.clamp(i, 0, 10)); // 10
  System.out.println(Math.clamp(d, 0, 10)); // 0.0
  System.out.println(Math.clamp(f, 0, 10)); // 10.0
  System.out.println(Math.clamp(l, 0, 10)); // 10

indexOf

The String class gets two new methods: indexOf(int ch, int beginIndex, int endIndex) and indexOf(String str, int beginIndex, int endIndex). The existing indexOf methods search in the whole string or from a given start index. The two new methods limit the search range further by specifying an end index.

Besides complete control over the search range, they are safer to use than indexOf(int ch, int fromIndex) and indexOf(String str, int fromIndex), because they throw an exception on illegal search ranges.

  String test = "Hello World";

  int pos = test.indexOf("lo", 2, 5);
  System.out.println(pos);
  // 3

  pos = test.indexOf('l', 6, 10);
  System.out.println(pos);
  // 9
  
  // existing indexOf does not check range
  pos = test.indexOf('l', 12);
  System.out.println(pos);
  // -1

  // new indexOf throws exception
  pos = test.indexOf('l', 6, 12);
  // Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Range [6, 12) out of bounds for length 11

repeat

repeat(int codePoint, int count) and repeat(CharSequence cs, int count) have been added to java.lang.StringBuilder and java.lang.StringBuffer to simplify the appending of multiple copies of characters or strings.

  StringBuilder sb1 = new StringBuilder();
  sb1.repeat("Hello ", 5);
  System.out.println(sb1);
  // Hello Hello Hello Hello Hello

  StringBuilder sb2 = new StringBuilder();
  sb2.repeat('-', 5);
  sb2.append("HELLO");
  sb2.repeat('-', 5);
  System.out.println(sb2);
  // -----HELLO-----

splitWithDelimiters

New splitWithDelimiters() methods added to String and java.util.regex.Pattern.

Unlike the split() method, splitWithDelimiters() returns an alternation of strings and matching delimiters rather than just the strings.

  String input = "1,us,10.0";

  String[] parts = input.split(",");
  System.out.println(parts.length); // 3
  for (String part : parts) {
      System.out.println(part);
  }
  // 1
  // us
  // 10.0



  String[] partsWithDelimiter = input.splitWithDelimiters(",", 0);
  System.out.println(partsWithDelimiter.length); // 5
  for (String part : partsWithDelimiter) {
      System.out.println(part);
  }
  // 1
  // ,
  // us
  // ,
  // 10.0


  Pattern p = Pattern.compile(",");
  String[] items = p.splitWithDelimiters(input, 0);
  System.out.println(items.length); // 5

Emoji support

The following six new methods are added to java.lang.Character for getting Emoji character properties, which are defined in the Unicode Emoji Technical Standard (UTS #51):

The names of these methods can also be used in a regular expression pattern with the `p{IsXXX} construct.

  Pattern isEmoji = Pattern.compile("\\p{IsEmoji}");
  var matcher = isEmoji.matcher("\uD83D\uDE0D");
  boolean matches = matcher.matches(); // true

HttpClient AutoCloseable

The java.net.http.HttpClient is now auto-closable, and the following methods have been added.