Java 8: Division mit Rundung Richtung negativ unendlich

Die Ganzzahldivision in Java ist simpel gestrickt. Vereinfacht ausgedrückt: Konvertiere die Ganzzahlen in Fließkommazahlen, führe die Division durch und schneide alles hinter dem Komma ab. So ergeben zum Beispiel 3 / 2 = 1 und 9 / 2 = 4. Bei negativem Ergebnis, durch entweder negativen Dividenden oder Divisor, das gleiche Spiel: -9 / 2 = -4 und 9 / -2 = -4. Schauen wir uns einmal die Rundungen an.

Ist das Ergebnis einer Division positiv und mit Nachkommaanteil, so wird das Ergebnis durch das Abschneiden der Nachkommastellen ein wenig kleiner, also abgerundet. Wäre 3/2 bei Fließkommazahlen 1,5, ist es bei einer Ganzzahldivision abgerundet 1. Bei negativen Ergebnissen einer Division ist das genau anders herum. Denn durch das Abschneiden der Nachkommastellen wird die Zahl etwas größer. -3/2 ist genau genommen -1,5, aber bei der Ganzzahldivision -1. Doch -1 ist größer als -1,5. Java wendet ein Verfahren an, was gegen null rundet.

In Java 8 hat die Mathe-Klasse zwei neue Methoden bekommen, die bei negativem Ergebnis einer Division nicht gegen null runden, sondern gegen negativ unendlich, also auch in Richtung der kleineren Zahl, wie es bei den positiven Ergebnissen ist.

class java.lang.Math

– static int floorDiv(int x, int y)

– static long floorDiv(long x, long y)

Ganz praktisch heißt das: 4/3 = Math.floorDiv(4, 3) = 1, aber wo -4 / 3 = -1 ergibt, liefert Math.floorDiv(-4, 3) = -2.

Java bekommt unchecked IO-Ausnahmen und BufferedReader.lines()

Die neue Methode BuferedReader lines() liefert ein Stream von Strings, die mit den Bulk-Methoden der Lambda-Bibliothek verarbeitet werden können. Brian Goetz gibt unter http://mail.openjdk.java.net/pipermail/lambda-dev/2012-November/006545.html ein (nicht ganz fehlerfreies) Beispiel:

try (reader = new BR(new FR(file))) {
     reader.lines()
           .filter(e -> !startsWith("#"))
           .map(e -> e.toUpperCase())
           .forEach(...);
}

Der Changeset: ttp://hg.openjdk.java.net/lambda/lambda/jdk/rev/94d64473e8e6

JDK 8 Milestones

Nach http://openjdk.java.net/projects/jdk8/milestones sind das nun:

M1    2012/04/26    (b36)
117 Remove the Annotation-Processing Tool (apt)
M2    2012/06/14    (b43)
133 Unicode 6.1

M3    2012/08/02    (b50)
124 Enhance the Certificate Revocation-Checking API
130 SHA-224 Message Digests
131 PKCS#11 Crypto Provider for 64-bit Windows

Hier stehen wir, das soll kommen:

M4    2012/09/13   
105 DocTree API
121 Stronger Algorithms for Password-Based Encryption
129 NSA Suite B Cryptographic Algorithms

M5    2012/11/29   
106 Add Javadoc to javax.tools
110 New HTTP Client
111 Additional Unicode Constructs for Regular Expressions
112 Charset Implementation Improvements
113 MS-SFU Kerberos 5 Extensions
114 TLS Server Name Indication (SNI) Extension
119 javax.lang.model Implementation Backed by Core Reflection
122 Remove the Permanent Generation
128 BCP 47 Locale Matching
140 Limited doPrivileged
153 Launch JavaFX Applications

M6    2013/01/31        Feature Complete
101 Generalized Target-Type Inference
104 Annotations on Java Types
107 Bulk Data Operations for Collections
108 Collections Enhancements from Third-Party Libraries
109 Enhance Core Libraries with Lambda
115 AEAD CipherSuites
118 Access to Parameter Names at Runtime
120 Repeating Annotations
123 Configurable Secure Random-Number Generation
126 Lambda Expressions and Virtual Extension Methods
135 Base64 Encoding and Decoding
156 G1 GC: Reduce need for full GCs
160 Lambda-Form Representation for Method Handles

M7    2013/02/21        Developer Preview
2013/03/18        All Tests Run
2013/04/04        Rampdown start
2013/05/02        API/Interface Freeze
2013/05/16        Zero Bug Bounce
2013/06/13        Rampdown phase 2
M8    2013/07/05        Final Release Candidate
GA    2013/09/09        General Availability

Von Date-Time API nichts zu sehen!

Java 8 bekommt auch eine Optional-Klasse (wie Guava und Scala)

+/*

+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.

+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.

+ *

+ * This code is free software; you can redistribute it and/or modify it

+ * under the terms of the GNU General Public License version 2 only, as

+ * published by the Free Software Foundation. Oracle designates this

+ * particular file as subject to the "Classpath" exception as provided

+ * by Oracle in the LICENSE file that accompanied this code.

+ *

+ * This code is distributed in the hope that it will be useful, but WITHOUT

+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or

+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License

+ * version 2 for more details (a copy is included in the LICENSE file that

+ * accompanied this code).

+ *

+ * You should have received a copy of the GNU General Public License version

+ * 2 along with this work; if not, write to the Free Software Foundation,

+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

+ *

+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA

+ * or visit www.oracle.com if you need additional information or have any

+ * questions.

+ */

+package java.util;

+

+import java.util.functions.Factory;

+import java.util.functions.Mapper;

+

+/**

+ * Optional

+ *

+ * @author Brian Goetz

+ */

+public class Optional<T> {

+ private final static Optional<?> EMPTY = new Optional<>();

+

+ private final T value;

+ private final boolean present;

+

+ public Optional(T value) {

+ this.value = value;

+ this.present = true;

+ }

+

+ private Optional() {

+ this.value = null;

+ this.present = false;

+ }

+

+ public static<T> Optional<T> empty() {

+ return (Optional<T>) EMPTY;

+ }

+

+ public T get() {

+ if (!present)

+ throw new NoSuchElementException();

+ return value;

+ }

+

+ public boolean isPresent() {

+ return present;

+ }

+

+ public T orElse(T other) {

+ return present ? value : other;

+ }

+

+ public T orElse(Factory<T> other) {

+ return present ? value : other.make();

+ }

+

+ public<V extends Throwable> T orElseThrow(Factory<V> exceptionFactory) throws V {

+ if (present)

+ return value;

+ else

+ throw exceptionFactory.make();

+ }

+

+ public<V extends Throwable> T orElseThrow(Class<V> exceptionClass) throws V {

+ if (present)

+ return value;

+ else

+ try {

+ throw exceptionClass.newInstance();

+ }

+ catch (InstantiationException | IllegalAccessException e) {

+ throw new IllegalStateException("Unexpected exception: " + e, e);

+ }

+ }

+

+ public<V> Optional<V> map(Mapper<T, V> mapper) {

+ return present ? new Optional<>(mapper.map(value)) : Optional.<V>empty();

+ }

+

+ @Override

+ public boolean equals(Object o) {

+ if (this == o) return true;

+ if (o == null || getClass() != o.getClass()) return false;

+

+ Optional optional = (Optional) o;

+

+ if (present != optional.present) return false;

+ if (value != null ? !value.equals(optional.value) : optional.value != null) return false;

+

+ return true;

+ }

+

+ @Override

+ public int hashCode() {

+ int result = value != null ? value.hashCode() : 0;

+ result = 31 * result + (present ? 1 : 0);

+ return result;

+ }

+}


Überlaufe

Bei einigen mathematischen Fragestellungen muss sich feststellen lassen, ob Operationen wie die Addition, Subtraktion oder Multiplikation den Zahlenbereich sprengen, also etwa den Ganzzahlenbereich eines Integers von 32 Bit verlassen. Passt das Ergebnis einer Berechnung nicht in den Wertebereich einer Zahl, so wird dieser Fehler standardmäßig nicht von Java angezeigt; weder der Compiler noch die Laufzeitumgebung melden dieses Problem. Es gibt auch keine Ausnahme, Java hat keine eingebaute Überlaufkontrolle.

Beispiel

Mathematisch gilt a × a / a = a, also zum Beispiel 100 000 × 100 000 / 100 000 = 100 000. In Java ist das anders, da wir bei 100 000 × 100 000 einen Überlauf im int haben.

System.out.println( 100000 * 100000 / 100000 );     // 14100

liefert daher 14100. Wenn wir den Datentyp auf long erhöhen, indem wir hinter ein 100 000 ein L setzen, sind wir bei dieser Multiplikation noch sicher, da ein long das Ergebnis aufnehmen kann.

System.out.println( 100000L * 100000 / 100000 );    // 100000

Hinweis

Ein Test auf Überlauf könnte aussehen: boolean canMultiply(int a, int b) { return a * b / b == a; } reichen. Doch eine JVM kann das zu return a == a; optimieren und somit zu return true; machen, sodass der Test nicht funktioniert.

Überlauf erkennen

Für eine Operation wie die Addition oder Subtraktion lässt sich relativ leicht erkennen, ob das Ergebnis über das Ziel hinausschießt. Eine Möglichkeit ist, bei der Addition zweier ints diese erst auf long zu bringen und dann den long mit der Konstanten Integer.MAX_VALUE/Integer.MIN_VALUE zu vergleichen. Aber über die Interna brauchen wir uns keine großen Gedanken machen, denn ab Java 8 kommen neue Methoden hinzu, die eine Überlauferkennung ermöglichen. Die Methoden gibt es in Math und StrictMath:

· static int addExact(int x, int y)

· static long addExact(long x, long y)

· static int subtractExact(int x, int y)

· static long subtractExact(long x, long y)

· static int multiplyExact(int x, int y)

· static long multiplyExact(long x, long y)

· static int toIntExact(long value)

Alle Methoden werfen eine ArithmeticException, falls die Operation nicht durchführbar ist, die letzte, wenn (int)value != value ist. Leider deklariert Java keine Unterklassen wie UnderflowException oder OverflowException, und Java meldet nur alles vom Typ ArithmeticException mit der Fehlermeldung „xxx overflow“, auch wenn es eigentlich ein Unterlauf ist:

System.out.println( subtractExact( Integer.MIN_VALUE, 1 ) ); // ArithmeticException

Inselupdate: Vorzeichenlos arbeiten

Bis auf char sind in Java alle integralen Datentypen vorzeichenbehaftet und kodiert im Zweierkomplement. Bei einem byte stehen 8 Bit für die Kodierung eines Wertes zur Verfügung, jedoch sind es eigentlich nur 7 Bit, denn über ein Bit erfolgt die Kodierung des Vorzeichens. Der Wertebereich ist von -128 bis +127. Über einen Umweg ist es möglich, den vollen Wertebereich auszuschöpfen und so zu tun, als ob Java vorzeichenlose Datentypen hätte.

byte als vorzeichenlosen Datentyp nutzen

Eine wichtige Eigenschaft der expliziten Typanpassung bei Ganzzahltypen ist es, dass die überschüssigen Bytes einfach abgeschnitten werden. Betrachten wir die Typanpassung an einem Beispiel:

int l = 0xABCD6F;

byte b = (byte) 0xABCD6F;

System.out.println( Integer.toBinaryString( l ) ); // 101010111100110101101111

System.out.println( Integer.toBinaryString( b ) ); // 1101111

Liegt eine Zahl im Bereich von 0 bis 255, so kann ein byte diese durch seine 8 Bit grundsätzlich speichern. Java muss jedoch mit der expliziten Typanpassung gezwungen werden, das Vorzeichenbit zu ignorieren. Erst dann entspricht die Zahl 255 acht gesetzten Bits, denn sie mit byte b = 255; zu belegen, funktioniert nicht.

Damit die Weiterverarbeitung gelingt, muss noch eine andere Eigenschaft berücksichtigt werden. Sehen wir uns dazu folgende Ausgabe an:

byte b1 = (byte) 255;

byte b2 = -1;

System.out.println( b1 ); // -1

System.out.println( b2 ); // -1

Das Bitmuster ist in beiden Fällen gleich, alle Bits sind gesetzt. Dass die Konsolenausgabe aber negativ ist, hat mit einer anderen Java-Eigenschaft zu tun: Java konvertiert das Byte, welches vorzeichenbehaftet ist, in ein int (der Parametertyp bei toBinaryString() ist int) und bei dieser Konvertierung wandert das Vorzeichen weiter. Das folgende Beispiel zeigt das bei der Binärausgabe:

byte b = (byte) 255;
int  i = 255;
System.out.printf( "%d %s%n", b, Integer.toBinaryString(b) );

// –1  11111111111111111111111111111111
System.out.printf( "%d %s%n", i, Integer.toBinaryString(i) );

// 255                         11111111

Die Belegung der unteren 8 Bit vom byte b und int i ist identisch. Aber während beim int die oberen 3 Byte wirklich null sind, füllt Java durch die automatische Anpassung des Vorzeichens bei der Konvertierung von byte nach int im Zweierkomplement auf die oberen drei Byte mit 255 auf. Soll ohne Vorzeichen weitergerechnet werden stört das. Diese Automatisch Anpassung nimmt Java immer vor, wenn mit byte/short gerechnet wird und nicht nur wie in unserem Beispiel, wenn eine Methoden den Datentyp int fordert.

Um bei der Weiterverarbeitung einen Datenwert zwischen 0 und 255 zu bekommen, also das Byte eines int vorzeichenlos zu sehen, schneiden wir mit der Und-Verknüpfung die unteren 8 Bit heraus – alle anderen Bits bleiben also ausgenommen:

byte b = (byte) 255;

System.out.println( b ); // -1

System.out.println( b & 0xff ); // 255

Bibliotheksmethoden für vorzeichenlose Behandlung

Immer ein & 0xff an einen Ausdruck zu setzen um die oberen Bytes auszublenden ist zwar nicht sonderlich aufwändig, aber schön ist das auch nicht. Hübscher sind Methoden wie toUnsignedInt(byte), die mit dem Namen deutlich dokumentieren, was hier eigentlich passiert. In Java 8 gibt es daher einige Neuerungen.

Neue Methoden in Byte:

  • static int toUnsignedInt(byte x)
  • static long toUnsignedLong(byte x)
  • In Integer:

  • static long toUnsignedLong(int x)
  • static String toUnsignedString(int i, int radix)
  • static String toUnsignedString(int i)
  • static int parseUnsignedInt(String s, int radix)
  • static int compareUnsigned(int x, int y)
  • static int divideUnsigned(int dividend, int divisor)
  • static int remainderUnsigned(int dividend, int divisor)
  • In Long:

  • String toUnsignedString(long i, int radix)
  • static String toUnsignedString(long i)
  • static long parseUnsignedLong(String s, int radix)
  • static int compareUnsigned(long x, long y)
  • static long divideUnsigned(long dividend, long divisor)
  • static long remainderUnsigned(long dividend, long divisor)
  • In Short:

  • static int toUnsignedInt(short x)
  • static long toUnsignedLong(short x)
  • Neben den einfachen Methoden toUnsignedXXX()-Methoden in den Wrapper-Klassen gesellen sich Methoden hinzu, die auch die Konvertierung in einem String bzw. das Parsen eines Strings ermöglichen. Bei Integer und Long lassen sich ebenfalls neue Methoden ablesen, die Vergleiche, Division und Restwertbildung vorzeichenlos durchführen.

    JDK-Interna: Removes the use of shared character array buffers by String along with the two fields needed to support the use of shared buffers.

    Eine recht fette Änderung, die großen Einfluss auf die Performace von substring-Operationen hat. Denn die ist nun nicht mehr so schnell wir früher O(1), sondern O(length()). Dafür spart man Speicher bei jedem String-Objekt, und davon gibt es zur Laufzeit einer normalen Anwendung wirklich viele. Reduktion: zwei ints, also 2 x 4 Byte pro String-Objekt.

    Implementierung: http://hg.openjdk.java.net/jdk8/tl/jdk/rev/2c773daa825d

    Erster Draft für “Annotations on Java Types” (JSR-308)

    http://jcp.org/aboutJava/communityprocess/edr/jsr308/index2.html. Aus der Spezi:

    JSR 308 extends Java to allow annotations on any use of a type, and on type parameter declarations.

    Beispiele:

    for generic type arguments to parameterized classes:
    Map<@NonNull String, @NonEmpty List<@Readonly Document>> files;
    for generic type arguments in a generic method or constructor invocation:
    o.<@NonNull String>m("…");
    for type parameter bounds, including wildcard bounds:
    class Folder<F extends @Existing File> { … }
    Collection<? super @Existing File>
    for class inheritance:
    class UnmodifiableList<T> implements @Readonly List<@Readonly T> { … }
    for throws clauses:
    void monitorTemperature() throws @Critical TemperatureException { … }
    for typecasts:
    myString = (@NonNull String) myObject;
    It is not permitted to omit the Java type, as in myString = (@NonNull) myObject;.
    for constructor invocation results (that is, for object creation):
    new @Interned MyObject()
    new @NonEmpty @Readonly List<String>(myNonEmptyStringSet)
    myVar . new @Tainted NestedClass
    For generic constructors (JLS §8.8.4), the annotation follows the explicit type arguments (JLS §15.9):
    new <String> @Interned MyObject()
    for type tests:
    boolean isNonNull = myString instanceof @NonNull String;
    It is not permitted to omit the Java type, as in myString instanceof @NonNull.

    Damit einher geht eine krasse Änderung, das bei Objektmethoden einfach so ein this Übergeben werden kann.

    It is permitted to explicitly declare the method receiver as the first formal parameter

    As an example, here are the standard definitions for toString and equals:
    class MyClass {

    public String toString() { … }
    public boolean equals(Object other) { … }
    }
    It is equivalent to write instead
    class MyClass {

    public String toString(MyClass this) { … }
    public boolean equals(MyClass this, Object other) { … }
    }
    and then it would be possible to annotate the receiver’s type:
    class MyClass {

    public String toString(@Readonly MyClass this) { … }
    public boolean equals(@Readonly MyClass this, @Readonly Object other) { … }
    }

    Interessant finde ich noch:

    To ease the transition from standard Java SE 7 code to code with type annotations, the reference implementation
    recognizes the type annotations when surrounded by comment markers:
    List</*@Readonly*/ Object> myList;
    This permits use of both standard Java SE 7 tools and the new annotations even before Java SE 8 is released