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.
领英推荐
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