On September 20, 2022, Java version 19 was released. In this article, I will try to present the most interesting changes related to the new version.
1. HashMap initialization.
Usually developers generate a HashMap like this:
Map<String, Integer> map = new HashMap<>(120);
Initially, it can be said that such initialization will guarantee us 120 mappings. Nothing could be further from the truth! Note that the hashmap is initialized with a default load factor of 0.75, i. e. when the hashmap is 75% full, it is rebuilt with twice the size. This guarantees that the elements are distributed as evenly as possible and that as few hashmap buckets contain more than one element.
So in fact the hashmap initialized above will not hold 120 mappings but only 90 (because 120 x 0.75 = 90). Now if we wanted to create a hashmap that would hold 120 mappings, we would have to calculate the number of mappings by dividing the initialized value by the load factor (120 / 0.75 = 160).
Java 19 allows you to create a map right away, taking into account the load factor, because the calculation is performed by the newHashMap method for us:
Map<String, Integer> map = HashMap.newHashMap(120);
A detailed description of what happens behind this method can be found in the HashMap.java class in IntelliJ:
/**
* Calculate initial capacity for HashMap based classes, from expected size and default load factor (0.75).
*
* @param numMappings the expected number of mappings
* @return initial capacity for HashMap based classes.
* @since 19
*/
static int calculateHashMapCapacity(int numMappings) {
return (int) Math.ceil(numMappings / (double) DEFAULT_LOAD_FACTOR);
}
/**
* Creates a new, empty HashMap suitable for the expected number of mappings.
* The returned map uses the default load factor of 0.75, and its initial capacity is
* generally large enough so that the expected number of mappings can be added
* without resizing the map.
*
* @param numMappings the expected number of mappings
* @param <K> the type of keys maintained by the new map
* @param <V> the type of mapped values
* @return the newly created map
* @throws IllegalArgumentException if numMappings is negative
* @since 19
*/
public static <K, V> HashMap<K, V> newHashMap(int numMappings) {
return new HashMap<>(calculateHashMapCapacity(numMappings));
}
2. Pattern Matching for a switch (preview).
The Guarded Pattern syntax has been changed in the latest version of Java. This means that instead of the condition String text && text.length > 15 we now use the keyword when.
Before Java 19:
switch(exampleObject) { case String text && text.length() > 15 -> System.out.println(text.toLowerCase()); case String text -> System.out.println(text.toLowerCase()); case Integer value -> System.out.println(value * value); default -> {} }
Currently:
switch(exampleObject) { case String text when text.length() > 15 -> System.out.println(text.toLowerCase()); case String text -> System.out.println(text.toLowerCase()); case Integer value -> System.out.println(value * value); default -> {} }
3. Record Patterns (preview)
In the latest version, we have introduced patterns for records, so we can now (in preview mode) use a record together with the instanceof operator. The following example, in which we have a Point record, checks whether a given object is of type Point:
//Record public record Point(int x, int y) {} // Pattern matching within instanceof private void doSomething(Object exampleObject) { if (exampleObject instanceof Point(int x, int y)) { System.out.println("exampleObject exists at point, x = " + x + ", y = " + y); } ... }
An example using the switch expression:
// Record public record Point(int x, int y) {} // Pattern matching within switch private void doSomething(Object exampleObject) { switch (exampleObject) { case Point(int x, int y) -> System.out.println("exampleObject exists at point, x = " + x + ", y = " + y); ... } }
You can also apply Nested Record Patterns.
4. Structural concurrency (incubator)
The main purpose of structured concurrency is to simplify multi-threaded programming with a structured API dedicated to concurrency. Multiple tasks running in different threads are treated as a single unit of work, which improves error handling and cancellation. More details on this can be found at this link.
5. Vector API (incubator)
The Vector API has been part of the JDK since version 16 as an incubator, further developed in Java 17 and 18. Don’t confuse it with the java.util.Vector class. Vector API is used for mathematical calculations of vectors and its mapping to modern SIMD (Single-Instruction-Multiple-Data) processors. In Java 19, the API was extended with the ability to store vectors and read them from memory segments (Foreign Function & Memory API).
Summary
Java 19 offers us most of the changes that are in preview or incubator mode. This does not change the fact that the changes are aimed at simplifying the use of the language. You can read about all the changes in Java 19 by clicking on link.
If you want to know more about Java or other topics connected with IT, outsourcing or startup, check our blog!
Sources: