Java 12 - New Features
New String and Files methods
String.indent()
To indent a string, we used to write a small helper method that put the desired number of spaces in front of the String. If it should work over multiple lines, the method became correspondingly complex.
Java 12 has such a method built-in: String.indent(). The following example shows how to indent a multiline string by four spaces:
String s = "I am\na multiline\nString.";
System.out.println(s);
System.out.println(s.indent(4));
The program prints the following:
I am
a multiline
String.
I am
a multiline
String.
String.transform()
The new String.transform() method applies an arbitrary function to a String and returns the function's return value. Here are a few examples:
String uppercase = "abcde".transform(String::toUpperCase);
Integer i = "12345".transform(Integer::valueOf);
BigDecimal big = "1234567891011121314151617181920".transform(BigDecimal::new);
When you look at the source code of String.transform(), you will notice that there is no rocket science at work. The method reference is interpreted as a function, and the String is passed to its apply() method:
public <R> R transform(Function<? super String, ? extends R> f) {
return f.apply(this);
}
Then why use transform() instead of just writing the following?
String uppercase = "abcde".toUpperCase();
Integer i = Integer.valueOf("12345");
BigDecimal big = new BigDecimal("1234567891011121314151617181920");
The advantage of String.transform() is that the function to be applied can be determined dynamically at runtime, while in the latter notation, the conversion is fixed at compile time.
Files.mismatch()
You can use the Files.mismatch() method to compare the contents of two files.
The method returns -1 if both files are the same. Otherwise, it returns the position of the first byte at which both files differ. If one of the files ends before a difference is detected, the length of that file is returned.
Recommended by LinkedIn
The Teeing Collector
For some requirements, you may want to terminate a Stream with two collectors instead of one and combine the result of both collectors.
In the following example source code, we want to determine the difference from largest to smallest number from a stream of random numbers (we use Optional.orElseThrow() introduced in Java 10 to avoid a "code smell" blaming):
Stream<Integer> numbers = new Random().ints(100).boxed();
int min = numbers.collect(Collectors.minBy(Integer::compareTo)).orElseThrow();
int max = numbers.collect(Collectors.maxBy(Integer::compareTo)).orElseThrow();
long range = (long) max - min;
The program compiles but aborts at runtime with an exception:
Exception in thread "main" java.lang.IllegalStateException:
stream has already been operated upon or closed
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at eu.happycoders.sandbox.TeeingCollectorTest.main(TeeingCollectorTest.java:12)
The exception text lets us know that we may terminate a Stream only once.
How can we solve this task then?
One variant would be to write a custom collector that accumulates minimum and maximum in a 2-element int array:
Stream<Integer> numbers = new Random().ints(100).boxed();
int[] result =
numbers.collect(
() -> new int[] {Integer.MAX_VALUE, Integer.MIN_VALUE},
(minMax, i) -> {
if (i < minMax[0]) minMax[0] = i;
if (i > minMax[1]) minMax[1] = i;
},
(minMax1, minMax2) -> {
if (minMax2[0] < minMax1[0]) minMax1[0] = minMax2[0];
if (minMax2[1] > minMax1[1]) minMax1[1] = minMax2[1];
});
long range = (long) result[1] - result[0];
This approach is quite complex and not very legible.
We can do it easier using the "Teeing Collector" introduced in Java 12. We can specify two collectors (called downstream collectors) and a merger function that combines the results of the two collectors:
Stream<Integer> numbers = new Random().ints(100).boxed();
long range =
numbers.collect(
Collectors.teeing(
Collectors.minBy(Integer::compareTo),
Collectors.maxBy(Integer::compareTo),
(min, max) -> (long) max.orElseThrow() - min.orElseThrow()));
Support for Compact Number Formatting
Using the static method NumberFormat.getCompactNumberInstance(), we can create a formatter for the so-called "compact number formatting". This is a form that is easy for humans to read, such as "2M" or "3 billion".
The following example shows how some numbers are displayed – once in the short and once in the long compact form:
NumberFormat nfShort =
NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
NumberFormat nfLong =
NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.LONG);
System.out.println(" 1,000 short -> " + nfShort.format(1_000));
System.out.println(" 456,789 short -> " + nfShort.format(456_789));
System.out.println(" 2,000,000 short -> " + nfShort.format(2_000_000));
System.out.println("3,456,789,000 short -> " + nfShort.format(3_456_789_000L));
System.out.println();
System.out.println(" 1,000 long -> " + nfLong.format(1_000));
System.out.println(" 456,789 long -> " + nfLong.format(456_789));
System.out.println(" 2,000,000 long -> " + nfLong.format(2_000_000));
System.out.println("3,456,789,000 long -> " + nfLong.format(3_456_789_000L));
The program will print the following:
1,000 short -> 1K
456,789 short -> 457K
2,000,000 short -> 2M
3,456,789,000 short -> 3B
1,000 long -> 1 thousand
456,789 long -> 457 thousand
2,000,000 long -> 2 million
3,456,789,000 long -> 3 billion