Unboxing dans Java moderne

Désormais, de nouvelles versions de Java sont publiées tous les six mois. De temps en temps, de nouvelles fonctionnalités y apparaissent: var en Java 10, changer d'expressions en Java 14, enregistrements et modèles en Java 16. Bien sûr, de nombreux articles, articles de blog ont été écrits à ce sujet, et de nombreux rapports ont été réalisés lors de conférences. Il s'est avéré, cependant, que nous avons tous manqué une mise à niveau de langage très intéressante qui s'est produite dans Java 14 - une mise à niveau de la boucle for régulière sur un ensemble d'entiers. Le fait est que cette mise à niveau n'a pas eu lieu dans le langage, mais dans la machine virtuelle, mais elle a considérablement influencé la façon dont nous pouvons programmer en Java.





Rappelons-nous la bonne vieille boucle for:





for (int i = 0; i < 10; i++) {
  System.out.println(i);
}
      
      



Cette syntaxe présente de nombreux inconvénients. Tout d'abord, la variable de boucle est mentionnée trois fois. Il est très facile de se tromper et de mentionner la mauvaise variable à un ou deux endroits. Deuxièmement, une telle variable n'est pas effectivement définitive. Il ne sera pas explicitement transmis aux lambdas ou aux classes anonymes. Mais plus important encore: vous ne pouvez pas être assuré contre la modification accidentelle d'une variable à l'intérieur d'une boucle. La lecture du code est également difficile. Si le corps de la boucle est volumineux, il n’est pas si facile de dire s’il change également à l’intérieur de la boucle, ce qui signifie que ce n’est pas clair, nous faisons simplement le tour des nombres dans l’ordre ou faisons quelque chose de plus compliqué. Il existe également des erreurs potentielles si vous devez changer la direction de la boucle ou activer la bordure. Et ça a l'air démodé.





De nombreux langages se sont déjà éloignés du lourd héritage de C et offrent une syntaxe plus moderne où vous spécifiez simplement une plage de nombres. Par exemple, prenez 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>



.








All Articles