現在、Javaの新しいバージョンが6か月ごとにリリースされています。時々、新しい機能がそれらに現れます:Java 10のvar、Java 14のスイッチ式、Java 16のレコードとパターン。もちろん、これについて多くの記事、ブログ投稿が書かれ、会議で多くの報告がなされています。しかし、Java 14で発生した非常に優れた言語アップグレードの1つ、つまり一連の整数に対する通常のforループのアップグレードを見逃していたことが判明しました。実際、このアップグレードは言語ではなく仮想マシンで行われましたが、Javaでのプログラミング方法に大きな影響を与えました。
古き良きforループを思い出しましょう:
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
この構文には多くの欠点があります。まず、ループ変数について3回言及します。混乱して、1つか2つの場所で間違った変数に言及するのは非常に簡単です。第二に、そのような変数は事実上最終的なものではありません。ラムダまたは匿名クラスに明示的に渡されることはありません。しかし、さらに重要なのは、ループ内の変数を誤って変更することに対して保険をかけることはできないということです。コードを読むことも難しいです。ループの本体が大きい場合、ループ内でも変化するかどうかを判断するのは簡単ではありません。つまり、明確ではありません。数字を順番に回るか、もっと複雑なことをします。ループの方向を変更したり、境界線をオンにしたりする必要がある場合にも、潜在的なエラーが発生します。そして、それは古風に見えます。
多くの言語はすでにCの重い遺産から離れており、数値の範囲を指定するだけのより現代的な構文を提供しています。たとえば、Kotlinを考えてみましょう。
for (x in 0 until 10) {
println(x)
}
: , , , . .
Java? , for-each, Java 5. :
/**
*
* @param fromInclusive ()
* @param toExclusive ( )
* @return Iterable, fromInclusive toExclusive.
*/
public static Iterable<Integer> range(int fromInclusive,
int toExclusive) {
return () -> new Iterator<Integer>() {
int cursor = fromInclusive;
public boolean hasNext() { return cursor < toExclusive; }
public Integer next() { return cursor++; }
};
}
rocket science, , . Java:
for (int i : range(0, 10)) { //
System.out.println(i);
}
. final, . . ? - . JMH-:
@Param({"1000"})
private int size;
@Benchmark
public int plainFor() {
int result = 0;
for (int i = 0; i < size; i++) {
result += i * i * i;
}
return result;
}
@Benchmark
public int rangeFor() {
int result = 0;
for (int i : range(0, size)) {
result += i * i * i;
}
return result;
}
, - , JIT- . , JIT . Java 8 :
Benchmark (size) Mode Cnt Score Error Units
BoxedRange.plainFor 1000 avgt 30 622.679 ± 7.286 ns/op
BoxedRange.rangeFor 1000 avgt 30 3591.052 ± 792.159 ns/op
range : 3,5 0,6 . -prof gc
, , rangeFor 13952 , plainFor . , , , 127 . Integer 128-999, 872 16 . , , Iterable, Iterator : (scalar replacement). .
, for , Java . Java:
: Java 14 range ! JIT- , , .
. Java 8 JVM -XX:+UnlockExperimentalVMOptions -XX:+AggressiveUnboxing
. , , :
Java 8-11 0,9 , 12 0,8, 13 . Java 14 , . , . , , .
? - Integer 127. valueOf ( Java 16):
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
, IntegerCache.low IntegerCache.high , . , , : . AggressiveUnboxing JIT- , , . , - :
Field field = Class.forName("java.lang.Integer$IntegerCache").getDeclaredField("cache");
field.setAccessible(true);
Integer[] arr = (Integer[]) field.get(null);
arr[130] = new Integer(1_000_000);
for (int i = 0; i < 10000; i++) {
int res = rangeFor();
if (res != -1094471800) {
System.out.println("oops! " + res + "; i = " + i);
break;
}
}
, , . Java if
. AggressiveUnboxing
oops! 392146832; i = 333
JIT- C2 rangeFor, , , , .
, Java 12 cmp r10d,7fh
, 127 (=0x7f). , Java 13. , , , - . , Java 12 rangeFor 8 , Java 13 16 , plainFor.
, : Java 14, . for (int i : range(0, 10))
Java for (int i = 0; i < 10; i++)
, .
Valhalla. Iterable<int>
, . , range . Iterable<Integer>
.