Unterschiede suchen mit Arrays.mismatch (…)

Neu in Java 9 sind weiterhin diverse Methoden

  • int mismatch(XXX[] a, XXX[] b)
  • int mismatch(XXX[] a, int aFromIndex, int aToIndex, XXX[] b, int bFromIndex, int bToIndex)

Sie geben den Index auf das erste Element zurück was ungleich ist. Sind beide Felder gleich ist die Rückgabe -1.

Für Objekt-Array gibt es weiterhin:

  • int mismatch(Object[] a, Object[] b)
  • int mismatch(Object[] a, int aFromIndex, int aToIndex, Object[] b, int bFromIndex, int bToIndex)
  • <T> int mismatch(T[] a, T[] b, Comparator<? super T> cmp)
  • <T> int mismatch(T[] a, int aFromIndex, int aToIndex, T[] b, int bFromIndex, int bToIndex, Comparator<? super T> cmp)

Die erste/zweite Methode nutzt direkt equals(…), die dritte/vierte einen externen Comparator.

Lexikografische Array-Verbleiche mit compare (…) und compareUnsigned(…)

Diverse in Java 9 eingeführte int compareXXX(XXX[] a, XXX[] b)-Methoden gehen die Arrays ab und testen alle Paare auf ihre Ordnung. Es gibt die von Comparator bekannte Rückgabe: ist jedes a[i] == b[i] ist die Rückgabe 0. Ist in der Abfragefolge ein a[i] kleiner als b[i] ist, dann ist die Rückgabe negativ, ist ein a[i] größer als b[i] ist die Rückgabe positiv. Die Methode ist überladen mit einer Variante, die einen Bereich im Array auswählt: compare(XXX[] a, int aFromIndex, int aToIndex, XXX[] b, int bFromIndex, int bToIndex). Für byte, short, int und long gibt es weiterhin eine Vergleichsmethode ohne Vorzeichen über den gesamten Wertebereich:

  • int compareUnsigned(XXX[] a, XXX[] b)
  • int compareUnsigned(XXX[] a, int aFromIndex, int aToIndex, XXX[] b, int bFromIndex, int bToIndex)

Für Objekte gibt es eigene Methoden:

  • static <T extends Comparable<? super T>> int compare(T[] a, T[] b)
    Vergleiche zwei Objekt-Arrays, wobei der Comparator die Gleichheit der Objektpaare feststellt.
  • static <T extends Comparable<? super T>> int compare(T[] a, int aFromIndex, int aToIndex, T[] b, int bFromIndex, int bToIndex)
    Vergleiche von Ausschnitten.
  • static <T> int compare(T[] a, T[] b, Comparator<? super T> cmp)
  • static <T> int compare(T[] a, int aFromIndex, int aToIndex,T[] b, int bFromIndex, int bToIndex, Comparator<? super T> cmp)
    Vergleicht mit Hilfe eines externen Comparator-Objekts.

Objekt-Arrays mit Arrays.equals(…) und Arrays.deepEquals(…) vergleichen

Die Arrays.equals(…)-Methode kann auch beliebige Objektfelder vergleichen, doch nutzt sie dann nicht die Identitätsprüfung per ==, sondern die Gleichheit per equals(…). Eine seit Java 9 hinzugekommene Methode fragt einen Comparator.

Beispiel

Enthalten zwei String-Arrays die gleichen Wörter, wobei Groß-/Kleinschreibung keine Rolle spielt?

String[] words1 = { "Zufriedenheit", "übertrifft" , "Reichtum" };

String[] words2 = { "REICHTUM", "übertrifft" , "ZuFRIEDEnheit" };

Arrays.sort( words1, String.CASE_INSENSITIVE_ORDER );

Arrays.sort( words2, String.CASE_INSENSITIVE_ORDER );

System.out.println( Arrays.equals( words1, words2, String.CASE_INSENSITIVE_ORDER ) );

class java.util.Arrays

  • staticbooleanequals(Object[]a,Object[]a2)
    Vergleicht zwei Arrays mit Objektverweisen. Ein Objekt-Array darf null enthalten; dann gilt für die Gleichheit e1==null ? e2==null : equals(e2).
  • staticbooleandeepEquals(Object[]a1,Object[]a2)
    Liefert true, wenn die beiden Arrays ebenso wie alle Unter-Arrays – rekursiv im Fall von Unter-Objekt-Arrays – gleich sind.
  • static <T> boolean equals(T[] a, T[] a2, Comparator<? super T> cmp)
    Vergleicht zwei Arrays und compare(a[i], b2[i]) muss für alle Pärchen 0 sein, damit beide Elemente als gleich gelten. Neu in Java 9.
  • static <T> boolean equals(T[] a, int aFromIndex, int aToIndex, T[] b, int bFromIndex, int bToIndex, Comparator<? super T> cmp)
    Vergleiche Ausschnitte von Arrays mit einem Comparator. Neu in Java 9.

Arrays mit Arrays.equals(…) und Arrays.deepEquals(…) vergleichen

Die statischen Methoden Arrays.equals(…) vergleichen, ob zwei Arrays die gleichen Inhalte besitzen; dazu ist die überladene Methode für alle wichtigen Typen definiert. Wenn zwei Arrays tatsächlich die gleichen Inhalte besitzen, ist die Rückgabe der Methode true, sonst false. Natürlich müssen beide Arrays schon die gleiche Anzahl von Elementen besitzen, sonst ist der Test sofort vorbei und das Ergebnis false. Im Fall von Objekt-Arrays nutzt Arrays.equals(…) nicht die Identitätsprüfung per ==, sondern die Gleichheit per equals(…).

Beispiel

Vergleiche drei Arrays:

int[] array1 = { 1, 2, 3, 4 };

int[] array2 = { 1, 2, 3, 4 };

int[] array3 = { 9, 9, 2, 3, 9 };

System.out.println( Arrays.equals( array1, array2 ) );              // true

System.out.println( Arrays.equals( array2, 1, 3, array3, 2, 4 ) );  // true

Ein Vergleich von Teil-Arrays ist erst in Java 9 hinzugekommen.

Bei unterreferenzierten Arrays betrachtet Arrays.equals(…) das innere Array als einen Objektverweis und vergleicht es auch mit equals(…) – was jedoch bedeutet, dass nicht identische, aber mit gleichen Elementen referenzierte innere Arrays als ungleich betrachtet werden. Die statische Methode deepEquals(…) bezieht auch unterreferenzierte Arrays in den Vergleich ein.

Beispiel

Unterschied zwischen equals(…) und deepEquals(…):

int[][] a1 = { { 0, 1 }, { 1, 0 } };
 int[][] a2 = { { 0, 1 }, { 1, 0 } };
 System.out.println( Arrays.equals( a1, a2 ) );     // false
 System.out.println( Arrays.deepEquals( a1, a2 ) ); // true
 System.out.println( a1[0] );                       // zum Beispiel [I@10b62c9
 System.out.println( a2[0] );                       // zum Beispiel [I@82ba41

Dass die Methoden unterschiedlich arbeiten, zeigen die beiden letzten Konsolenausgaben: Die von a1 und a2 unterreferenzierten Arrays enthalten die gleichen Elemente, sind aber zwei unterschiedliche Objekte, also nicht identisch.

Hinweis

deepEquals(…) vergleicht auch eindimensionale Arrays:

Object[] b1 = { "1", "2", "3" };
 Object[] b2 = { "1", "2", "3" };
 System.out.println( Arrays.deepEquals( b1, b2 ) ); // true

class java.util.Arrays

  • staticbooleanequals(XXX[]a,XXX[]a2)
    Vergleicht zwei Arrays gleichen Typs und liefert true, wenn die Arrays gleich groß und Elemente paarweise gleich sind. XXX steht stellvertretend für boolean, byte, char, int, short, long, double, float.
  • staticbooleanequals(XXX[] a, int aFromIndex, int aToIndex, XXX[] b, int bFromIndex, int bToIndex)
    Vergleicht zwei Arrays, bleibt jedoch in den gewählten Ausschnitten. Neue Methoden in Java 9.
  • staticbooleanequals(Object[]a,Object[]a2)
    Vergleicht zwei Arrays mit Objektverweisen. Ein Objekt-Array darf null enthalten; dann gilt für die Gleichheit e1==null ? e2==null : equals(e2).
  • staticbooleandeepEquals(Object[]a1,Object[]a2)
    Liefert true, wenn die beiden Arrays ebenso wie alle Unter-Arrays – rekursiv im Fall von Unter-Objekt-Arrays – gleich sind.

Die Schnittstelle Checksum

Wir finden Zugang zur Prüfsummenberechnung über die Schnittstelle java.util.zip.Checksum, die für ganz allgemeine Prüfsummen steht. Eine Prüfsumme wird entweder für ein Feld oder ein Byte berechnet. Checksum liefert die Schnittstelle zum Initialisieren und Auslesen von Prüfsummen, die die konkreten Prüfsummen-Klassen implementieren müssen.

interface java.util.zip.Checksum

  • longgetValue()
    Liefert die aktuelle Prüfsumme.
  • voidreset()
    Setzt die aktuelle Prüfsumme auf einen Anfangswert.
  • voidupdate(intb)
    Aktualisiert die aktuelle Prüfsumme mit dem Byte in b.
  • voidupdate(byte[]b,intoff,intlen)
    Aktualisiert die aktuelle Prüfsumme mit den Bytes aus dem Array.
  • default public void update(byte[] b)
    Implementiert als update(b, 0, b.length); – neu in Java 9.
  • default public void update(ByteBuffer buffer)
    Aktualisiert die Prüfsumme mit den Bytes aus dem buffer. Neu in Java 9.

Die Standardbibliothek bietet bisher drei Klassen für die Prüfsummenberechnung als Implementierungen von Checksum:

  • util.zip.CRC32: CRC-32 basiert auf einer zyklischen Redundanzprüfung und testet etwa ZIP-Archive oder PNG-Grafiken. Nativ in C programmiert.
  • util.zip.CRC32C: CRC-32C nutzt ein anderes Polynom als CRC-32, verfolgt aber das gleiche Berechungsprinzip. Das JDK implementiert es in purem Java und nicht nativ. Die Ausführungszeit kann dennoch besser sein. Neu in Java 9.
  • util.zip.Adler32: Die Berechnung von CRC-32-Prüfsummen kostet viel Zeit. Eine Adler-32-Prüfsumme kann wesentlich schneller berechnet werden und bietet eine ebenso geringe Wahrscheinlichkeit, dass Fehler unentdeckt bleiben.

Die Klasse CRC32

Oft sind Polynome die Basis der Prüfsummenberechnung. Eine häufig für Dateien verwendete Prüfsumme ist CRC-32.

Nun lässt sich zu einer 32-Bit-Zahl eine Prüfsumme berechnen, die genau für diese 4 Byte steht. Damit bekommen wir aber noch keinen ganzen Block kodiert. Um das zu erreichen, berechnen wir den Wert eines Zeichens und XOR-verknüpfen den alten CRC-Wert mit dem neuen. Jetzt lassen sich beliebig Blöcke sichern. Die Berechnung ist insgesamt sehr zeitaufwändig, und Adler-32 stellt eine schnellere Alternative dar.

Beispiel

Die Klasse CRC32 berechnet eine Prüfsumme über alle durchlaufenden Bytes, die gereicht werden als einzelne Bytes oder Felder. In aller Kürze sieht ein Programm zur Berechnung von Prüfsummen für ein paar Eingaben folgendermaßen aus:

CRC32 crc = new CRC32();
crc.update( 1 );
crc.update( new byte[]{ 2, 3, 4, 5, 6, 7 } );
System.out.println( crc.getValue() ); // 1894017160

CRC32 implementiert nicht nur alle Methoden, sondern fügt noch zwei Methoden und natürlich einen Konstruktor hinzu:

Stream iterate(…)-Methoden

Die zwei statischen iterate(…)-Methoden generieren einen Stream aus einem Startwert und einer Funktion, die das nächste Element produziert.  Bei iterate(T seed,
UnaryOperator<T> f) ist der Strom unendlich, bei der zweiten – in Java 9 hinzugekommenen Methode – iterate(…, Predicate<? super T> hasNext, …)-Methode beendet ein erfülltes Prädikat den Strom und erinnert an eine klassische for-Schleife. Der Abbruch über ein Prädikat ist sehr flexibel, denn bei der ersten iterate(…)-Methode ist das Stoppen immer ein Problem und so folgt oftmals ein limit(…) oder takeWhile(…) zum Limitieren der Elemente.

Beispiele

Produziere Permutationen eines Strings.

UnaryOperator<String> shuffleOp = s -> {

  char[] chars = s.toCharArray();

  for ( int index = chars.length - 1; index > 0; index-- ) {

    int rndIndex = ThreadLocalRandom.current().nextInt( index + 1 );

    if ( index == rndIndex ) continue;

    char c = chars[ rndIndex ];

    chars[ rndIndex ] = chars[ index ];

    chars[ index ] = c;

  }

  return new String( chars );

};

String text = "Sie müssen nur den Nippel durch die Lasche ziehn";

Stream.iterate( text, shuffleOp ).limit( 10 ).forEach( System.out::println );

Erzeuge einen BigInteger-Stream ab 10 Millionen alle Zahlen aus, bis mit hoher Wahrscheinlichkeit eine Zufallszahlen erscheint.

Predicate<BigInteger> isNotPrime = i -> ! i.isProbablePrime( 10 );

UnaryOperator<BigInteger> incBigInt = i -> i.add( BigInteger.ONE );

Stream.iterate( BigInteger.valueOf( 10_000_000 ), isNotPrime, incBigInt )

      .forEach( System.out::println );

Präfix-Operation der Stream-API

Unter einem Präfix verstehen wir eine Teilfolge eines Streams, die beim ersten Element beginnt. Wir können mit limit(long) selbst einen Präfix generieren, doch im Allgemeinen geht es darum eine Bedingung zu haben, die alle Elemente eines Präfix-Streams erfüllen müssen, und wenn es für ein Element nicht gilt, dann den Stream zu beenden. Java 9 deklariert für dafür zwei neue Methoden takeWhile(…) und dropWhile(…):

  • default Stream<T> takeWhile(Predicate<? super T> predicate)
  • default Stream<T> dropWhile(Predicate<? super T> predicate)

Die deutsche Übersetzung von takeWhile(…) wäre „nimm solange predicate gilt“ und dropWhile(…) „lass fallen, solange predicate gilt“.

Beispiel: Der Stream soll bei Eintreffen des Wortes „Trump“ sofort enden:

new Scanner( "Dann twitterte Trump am 7. Nov. 2012: "

           + "'It's freezing and snowing in New York--we need global warming!'" )

  .useDelimiter( "\\P{Alpha}+" ).tokens()

  .takeWhile( s -> !s.equalsIgnoreCase( "trump" ) )

  .forEach( System.out::println );  // Dann twitterte

}

Der Stream soll nach dem längsten Präfix beginnen, nämlich dann, wenn eine Zahl negativ wurde:

Stream.of( 1, 2, -1, 3, 4, -1, 5, 6 )

      .dropWhile( i -> i > 0 )

      .forEach( System.out::println );    // -1 3 4 -1 5 6

Das Element, das das Prädikat erfüllt, ist selbst das erste Element im neuen Stream. Wir können es mit skip(1) überspringen.

Erfüllt schon bei takeWhile(…) das erste Element nicht das Prädikat ist die der Ergebnis-Stream leer, erfüllt kein Element die Bedingung ist das Ergebnis wie der Ursprungs-Stream. takeWhile(…) und dropWhile(…) können zusammen verwendet werden: so liefert Stream.of( 1, 2, -1, 3, 4, -1, 5, 6 ).dropWhile( i -> i > 0 ).skip( 1 ).takeWhile( i -> i > 0 ).forEach( System.out::println ); die Ausgaben 3 4.

Hinweis: Präfixe sind nur für geordnete Streams sinnvoll. Und wenn Streams parallel sind müssen sie für die Präfixberechnung wieder in Reihe gebracht werden, das ist eine eher teurer Operation.

Funktion zur String-Ersetzung einsetzen

Nach einem Teilstring über einen regulären Ausdruck zu suchen, und diesen dann nach einer Transformation wieder zurückzuschreiben ist eine häufige Operation, für die es in Java 9 zwei neue Methoden gibt.

  • String replaceFirst(Function<MatchResult, String> replacer)
  • String replaceAll(Function<MatchResult, String> replacer)

Beispiel

String s = Pattern.compile( "(\\d+)\\s+[€|EUR]+" )

                  .matcher( "2 Perlen, 10 € in bar und 10000 EUR auf dem Konto." )

                  .replaceAll( matchresult ->

                    matchresult.group( 1 ).length() < 4 ? "wenig Kohle" : "viel Kohle" );

System.out.println( s ); // 2 Perlen, wenig Kohle in bar und viel Kohle auf dem Konto.

Datumsklasse java.time.LocalDate

Ein Datum (ohne Zeitzone) repräsentiert die Klasse LocalDate. Damit lässt sich zum Beispiel ein Geburtsdatum repräsentieren.

Ein temporales Objekt kann über die statischen of(…)-Fabrikmethoden aufgebaut, über ofInstant(Instant instant, ZoneId zone) oder von einem anderen temporalen Objekt abgeleitet werden. Interessant sind die Methoden, die mit einem TemporalAdjuster arbeiten.

Beispiel

 LocalDate today = LocalDate.now();
 LocalDate nextMonday = today.with( TemporalAdjusters.next( DayOfWeek.SATURDAY ) );
 System.out.printf( "Heute ist der %s, und frei ist am Samstag, den %s",
                    today, nextMonday );

Mit den Objekten in der Hand können wir diverse Getter nutzen und einzelne Felder erfragen, etwa getDayOfMonth(), getDayOfYear() (liefern int) oder getDayOfWeek(), das eine Aufzählung vom Typ DayOfWeek liefert, und getMonth(), das eine Aufzählung vom Typ Month Weiterhin gibt es long toEpochDay() und und in Java 9 long toEpochSecond(LocalTime time, ZoneOffset offset).

Dazu kommen Methoden, die mit minusXXX(…) oder plusXXX(…) neue LocalDate-Objekte liefern, wenn zum Beispiel mit minusYear(long yearsToSubtract) eine Anzahl Jahre zurückgelaufen werden soll. Durch die Negation des Vorzeichens kann auch die jeweils entgegengesetzte Methode genutzt werden, sprich LocalDate.now().minusMonths(1) kommt zum gleichen Ergebnis wie LocalDate.now().plusMonths(-1). Die withXXX(…)-Methoden belegen ein Feld neu und liefern ein modifiziertes neues LocalDate-Objekt.

Von einem LocaleDate lassen sich andere temporale Objekte bilden, atTime(…) etwa liefert LocalDateTime-Objekte, bei denen gewisse Zeit-Felder belegt sind. atTime(int hour, int minute) ist so ein Beispiel. Mit until(…) lässt sich eine Zeitdauer vom Typ Period liefern. Interessant sind zwei neue Java 9-Methoden, die einen Strom von LocalDate-Objekten bis zu einem Endpunkt liefern.

  • Stream<LocalDate> datesUntil(LocalDate endExclusive)
  • Stream<LocalDate> datesUntil(LocalDate endExclusive, Period step)

java.time.Duration

Eine Klasse Duration repräsentiert Dauern von Zeiten und ist weder mit Zeitzonen verbunden noch mit anderen Zeitleisten. Daher ist auch ein Tag idealisiert exakt 24 Stunden lang, Schaltsekunden kennt die Klasse nicht, und einen Tag zu addieren heißt, 24 Stunden aufzurechnen.

Die interne Berechnungseinheit ist Sekunden bzw. Nanosekunden, auch wenn Hilfsmethoden Zeiteinheiten bis Stunden erlauben. Darüber wird eine Duration auch aufgebaut, über ofXXX()-Methoden wie ofSeconds(long seconds, long nanoAdjustment) oder ofDays(long days). Differenzen bildet wieder Duration between(Temporal startInclusive, Temporal endExclusive), wobei Temporal eine Schnittstelle ist, die etwa von LocalDate, LocalDateTime, LocalTime, OffsetDateTime, OffsetTime, ZonedDateTime, Year, YearMonth, Instant implementiert wird, nicht aber von Period.

Beispiel: Wie viel Zeit vergeht zwischen der Ausführung?

Instant start = Instant.now();
 try {
   Files.walk( Paths.get( System.getProperty( "user.home" ) ) ).count();
 } catch ( Exception e ) { }
 Instant end = Instant.now();
 System.out.println( Duration.between( start, end ).toMillis() + " ms" );

Abgewandelt wird eine Duration wieder über withXXX(…) oder die minusXXX(…)/plusXXX(…)-Methoden. Duration dividedBy(long divisor) teilt die Duration durch eine Zeit. Die neue Java 9-Methode long dividedBy(Duration divisor) sagt, wie oft der divisor in der Duration liegt.

toNanos(), toMillis(), toSeconds() – neu in Java 9 –, toMinutes(), toHours(), toDays() konvertieren in ein long, getNano() und getSeconds() liefern die zwei Bestandteile einer Duration als long. Wichtig ist der Unterschied: Die Getter liefern den Feldwert von Duration, während toXXX() immer konvertiert.

Beispiel

Duration aSecond = Duration.of( 1, ChronoUnit.MINUTES );
out.println( aSecond.getSeconds() ); // 60
out.println( aSecond.getNano() ); // 0
out.println( aSecond.toMinutes() ); // 1
out.println( aSecond.toSeconds() ); // 60
out.println( aSecond.toNanos() ); // 60000000000

In Java 9 kommen Methoden hinzu, die die Anteile an Minuten, Sekunden, usw. erfragen, und zwar long toDaysPart(), int toHoursPart(), int toMinutesPart(), int toSecondsPart(), int toMillisPart() und int toNanosPart().

UML-Diagramme mit Eclipse Papyrus

Papyrus (https://www.eclipse.org/papyrus/) ist ein Modellierungswerkeug, mit dem UML-Diagramme gezeichnet und generiert werden können.

Installation

  1. Help -> Install New Software mit Papyrus selbst:
    http://download.eclipse.org/modeling/mdt/papyrus/updates/releases/neon
    – „Papyrus“ auswählen -> installieren
  2. Help -> Install New Software mit: http://download.eclipse.org/modeling/mdt/papyrus/components/designer
    – Papyrus Designer Category“ aufklappen
    – „Papyrus Java profile library and code generation (Incubation)“ auswählen -> installieren

Weitere Schritte

  1. New -> Other… -> Papyrus -> Papyrus Model erstellen
  2. Windows -> Show View -> Model Explorer / oder Create View im Model
  3.  Im Model Explorer Rechtsklick auf das RootElement: New Diagram -> Class Diagram
  4. Anschließend können einzelne Klassen oder Pakete per Drag & Drop reingezogen werden.

Basics

Alle Elemente die man reingezogen hat können markiert werden um folgende Operationen durchzuführen:

  1. Rechtsklick -> Filters -> Show/Hide Contents : Anzeigen/Ausblenden von Attributen und Operationen etc.
  2. Rechtsklick -> Filters -> Show/Hide Related Links: Anzeigen/Ausblenden der Klassenbeziehungen zueinander
  3. Rechtsklick -> Arrange All : Ordnet alle Elemente übersichtlich innerhalb der Model an
  4. Rechtsklick -> Filters -> Show/Hide Compartments: Anzeigen/Ausblenden der Abteile (für Attribute/Operationen/“nested classifiers“ etc.)

Rock Around the java.time.Clock

Die zeitbezogenen temporalen Typen bekommen ihre Zeit von einer Uhr. Die Uhr kombiniert die aktuelle Zeit mit einer Zeitzone; Java repräsentiert sie durch den Typ java.time.Clock. Der Klasse ist abstrakt und Exemplare sind über statische Fabrikmethoden zu bekommen:

Statische Clock-Mehoden Rückgabe
systemUTC() Uhr mit genauster Zeit in der Greenwich/UTC Zeitzone
systemDefaultZone() Uhr mit genauster Zeit in der aktuellen Systemzeitzone
tickMillis(ZoneId zone) Uhr mit Millisekundenauflösung ohne Nanosekundenanteil in der gegeben Zeitzone (ab Java 9)
tickSeconds(ZoneId zone) Uhr mit Sekundenauflösung ohne Milli- und Nanosekundenanteil in der gegeben Zeitzone
tickMinutes(ZoneId zone) Uhr mit Minutenauflösung ohne Sekunden-, Milli- und Nanosekundenanteil in der gegeben Zeitzone
tick(Clock baseClock, Duration tickDuration) Uhr, die auf der Basis von baseClock in Abständen von tickDuration „tickt“
offset(Clock baseClock, Duration tickDuration) Neue Uhr, die gegenüber der baseClock um tickDuration verschoben ist
fixed(Instant fixedInstant, ZoneId zone) Voreingestellte Uhrzeit, die sie nie ändert

Clock-Exemplare erfragen

Die now()-Methoden der Klassen LocalTime, LocalDate, … nutzen standardmäßig die Uhr in der aktuellen Zeitzone:

public static LocalDate now() {

  return now(Clock.systemDefaultZone());

}

Abzulesen am Beispiel ist, dass die now(…)-Methode überladen ist, sodass ein Programm selbst eine Clock angeben kann. Das ist nützlich zum Testen, wenn zum Beispiel die Zeit immer die gleiche sein soll.

Unixzeit, System.currentTimeMillis(), der 1.1.1970

Der 1.1.1970

Der 1.1.1970 war ein Donnerstag mit wegweisenden Änderungen. In der katholischen Kirche wurde der Allgemeine Römische Kalender eingeführt,[1] und die Briten freuten sich, dass die Volljährigkeit von 24 Jahren auf 18 Jahre fiel. Zu etwas technischem: Der 1.1.1970 heißt auch Unix Epocheund eine Unixzeit wird relativ zu diesem Zeitpunkt in Sekunden beschrieben. So kommen wir 100.000.000 Sekunden nach dem 1.1.1970 beim 3. März 1973 um 09:46:40 aus. Das Unix Billennium wurde bei 1.000.000.000 Sekunden nach dem 1.1.1970 gefeiert, und repräsentiert den 9. September 2001, 01:46:40.

System.currentTimeMillis()

Auch für uns Java-Entwickler ist die Unixzeit von Bedeutung, denn viele Zeiten in Java sind relativ zu diesem Datum. Der Zeitstempel 0 bezieht sich auf den 1.1.1970 0:00:00 Uhr Greenwich-Zeit – das entspricht 1 Uhr nachts deutscher Zeit. Die Methode currentTimeMillis() liefert die vergangenen Millisekunden – nicht Sekunden! – relativ zum 1.1.1970, wobei allerdings die Uhr des Betriebssystems nicht so genau gehen muss. Die Anzahl der Millisekunden wird in einem long repräsentiert, also in 64 Bit. Das reicht für etwa 300 Millionen Jahre.

Warnung: Die Rückgaben von System.currentTimeMillis() sind nicht montoton steigend, weil die Systemuhr umgestellt werden kann, etwa durch eine Zeitkorrektur. Zeitmessungen dürfen daher nie als Differenz von zwei currentTimeMillis() berechnet werden. Eine vernünftge Alternative ist System.nanoTime().

[1]  Cäsar und der julianische Kalender liegen noch weiter zurück.

Socket-Optionen erfragen

Socket und ServerSocket unterstützen Optionen wie zum Beispiel einen Type of Service oder TCP-NoDelay, wobei diese von System zu System unterschiedlich sein können. In Java 9 lassen sich die Optionen einfach abfragen.

Beispiel: Gib alle Optionen aus:

Socket socket = new Socket( "localhost", 80 );

System.out.println( socket.supportedOptions().stream()

                          .map( o -> o.name() + " is " + o.type().getSimpleName() )

                          .collect( Collectors.joining( ", " ) ) );

// SO_LINGER is Integer, IP_TOS is Integer, SO_KEEPALIVE is Boolean, TCP_NODELAY is Boolean, SO_SNDBUF is Integer, SO_REUSEADDR is Boolean, SO_RCVBUF is Integer




ServerSocket serversocket = new ServerSocket( 8080 );

System.out.println( serversocket.supportedOptions() );

// [SO_RCVBUF, SO_REUSEADDR, IP_TOS]

Die supportedOptions()-Methoden liefern eine Set<SocketOption>. Eine SocketOption deklariert name() und type().

Mit yield() und onSpinWait() auf Rechenzeit verzichten

Im besten Fall signalisiert ein Ereignis, dass Daten bereit stehen und abgeholt werden können. Doch nicht immer gibt es eine solche API. Dann findet sich oft ein wiederholter Test auf eine Bedingung und wird diese wahr, stehen zum Beispiel Daten zum Abholen bereit. In Code sieht das so aus:

while ( ! daten_da )

  ;

datenVearbeiten();

Dieses aktive Warten, engl. busy waiting, oder auch spinning genannt, auf Daten (eng. polling) mithilfe einer spin-loop verschwendet so programmiert viel Rechenzeit. Eine Lösung ist mit dem bekannten sleep(…) eine Zeit von Millisekunden zu schlafen, wobei es schwierig ist, die richtige Anzahl an Schlafsekunden zu ermitteln. Zu kurz geschlafen heißt: noch einmal weiter pollen; zu lang geschlafen, heißt: es gibt eine unnötige Verzögerung.

Die Thread-Klasse bietet zwei weitere Methoden, um kooperative Threads zu programmieren: die Methode yield() und ab Java 9 onSpinWait(). Anders als bei sleep(…) gibt es hier keine Millisekunden anzugeben.

while ( ! daten_da )

  Thread.onSpinWait();

datenVearbeiten();

class java.lang.Thread
implements Runnable

  • static void onSpinWait()
    Signalisiert der Laufzeitumgebung. das der Thread in einer spin-loop auf Daten wartet. Dieser Hinweis kann die JVM an den Prozessor weitegeben und die Laufzeit ist bei typischen Systemen besser als mit yield(). Neu in Java 9 im Zuge der Umsetzung der „JEP 285: Spin-Wait Hints“.[1]
  • static voidyield()
    Der laufende Thread gibt freiwillig seine Rechenzeit ab, sodass er bezüglich seiner Priorität wieder in die Thread-Warteschlange des Systems einordnet wird. Einfach ausgedrückt, signalisiert yield() der Thread-Verwaltung: „Ich setze diese Runde aus und mache weiter, wenn ich das nächste Mal dran bin.“ Die Methode ist für Implementierungen der JVM nicht verbindlich. Die API-Dokumentation warnt eher von der Methode „It is rarely appropriate to use this method.”

[1]       http://openjdk.java.net/jeps/285

System-Logging in Java 9

Logging von Bibliotheken und eigener Software ist eine Sache, eine andere ist, dass die Java SE Bibliotheken und die JVM z. B. bei –verbose:gc selbst auch loggen. Bis vor Java 9 war nicht standardisiert wohin die Ausgaben gehen und konnten nicht problemlos über einen benutzerinstallierten Logger geschrieben werden. Java 9 setzt die „JEP 264: Platform Logging API and Service“ um und damit besorgt sich die Java SE-Implementierung über System.getLogger(…) einen System.Logger und schreibt über ihn Informationen in einem gewünschten Log-Level.

Wohin dieser System-Logger schreibt lässt sich konfigurieren, sodass zum Beispiel Log4j 2 statt dem Standard-Logger aus dem java.util.logging-Paket genommen wird. Das geschieht über die java.util.ServiceLoader-API; sie lädt eine Klasse, die LoggerFinder erweitert und abstrakte Methode public Logger getLogger(String name, Module module) so überschreibt, das eine Implementierung von java.lang.System.Logger zurückgegeben wird.

Multiplizieren von long-Ganzzahlen

Es gibt keinen primitiven Datentyp der mehr als 64 Bit (8 Byte) hat, sodass das Ergebnis von long * long mit seinen 128 Bit nur in ein BigInteger komplett passt. Allerdings erlaubt eine neue Methode aus Java 9, die oberen 64 Bit einer long-Multiplikation getrennt zu erfragen, und zwar mit der Methode multiplyHigh(long x, long y) in Math und StrictMath – wobei StrictMath nur auf Math leitet.

BigInteger v = BigInteger.valueOf( Long.MAX_VALUE )

                         .multiply( BigInteger.valueOf( Long.MAX_VALUE ) );

System.out.println( v );  // 85070591730234615847396907784232501249




long lowLong = Long.MAX_VALUE * Long.MAX_VALUE;

long highLong = Math.multiplyHigh( Long.MAX_VALUE, Long.MAX_VALUE );

BigInteger w = BigInteger.valueOf( highLong )

                         .shiftLeft( 64 )

                         .add( BigInteger.valueOf( lowLong ) );

System.out.println( w );  // 85070591730234615847396907784232501249

Multiplizieren von int-Ganzzahlen

Der *-Operator führt bei int keine Anpassung an den Datentypen durch, sodass die Multiplikation von zwei ints wiederum int liefert. Doch das Produkt kann schnell aus dem Wertebereich laufen, sodass es zum Überlauf kommt. Selbst wenn das Produkt in eine long-Variable geschrieben wird, erfolgt die Konvertierung von int in long erst nach der Multiplikation:

int  i = Integer.MAX_VALUE * Integer.MAX_VALUE;

long l = Integer.MAX_VALUE * Integer.MAX_VALUE;

System.out.println( i );     // 1

System.out.println( l );     // 1

Sollen zwei ints ohne Überlauf multipliziert werden, ist einer der beiden Faktoren auf long anzupassen, damit es zum korrekten Ergebnis 4611686014132420609 führt.

System.out.println( Integer.MAX_VALUE * (long) Integer.MAX_VALUE );

System.out.println( (long) Integer.MAX_VALUE * Integer.MAX_VALUE );

Da diese Typanpassung schnell vergessen werden kann und nicht besonders explizit ist, bieten die Klassen Math und StrictMath die statische Methode long multiplyFull(int x, int y), die für uns über (long)x * (long)y die Typumwandlung vornehmen.

Multiply-Accumulate, fma(…)

Die mathematische Operation a × b + c  kommt in Berechnungen oft vor, sodass Prozessoren heute diese Berechung optimiert – das heißt schneller und mit besserer Genauigkeit – durchführen können. In Java 9 gibt es bei Math und StrictMath die neue Methode fma(…) für die “fused multiply-accumulate“-Funktion aus dem IEEE 754 Standard.

class java.lang.Math
class java.lang.StrictMath

  • static double fma(double a, double b, double c)
  • static float fma(float a, float b, float c)
    Führt a × b + c mit dem HALF_EVEN durch. Gegenüber einem einfachen a * b + c berücksichtigt fma(…) die Besonderheiten Unendlich und NaN.

Rundungsmodus BigDecimal

Eine Besonderheit stellt die Methode divide(…) dar, die zusätzlich einen Rundungsmodus und optional auch eine Anzahl gültiger Nachkommastellen bekommen kann. Zunächst ohne Rundungsmodus:

BigDecimal a = new BigDecimal( "10" );
BigDecimal b = new BigDecimal( "2" );
System.out.println( a.divide(b) );   // 5

Es ist kein Problem, wenn das Ergebnis eine Ganzzahl oder das Ergebnis exakt ist:

System.out.println( BigDecimal.ONE.divide(b) );  // 0.5

Wenn das Ergebnis aber nicht exakt ist, lässt sich divide(…) nicht einsetzen. Die Anweisung new BigDecimal(1).divide( new BigDecimal(3) ) ergibt den Laufzeitfehler: „java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.“

An dieser Stelle kommen diverse Rundungsmodi ins Spiel, die bestimmen, wie die letzte Ziffer einer Rundung bestimmt werden soll. Sie lassen sich über eine Aufzählung java.math.RoundingMode übermitteln. Folgende Konstanten gibt es:

Konstante in RoundingMode Bedeutung
DOWN Runden nach 0
UP Runden weg von 0
FLOOR Runden nach negativ unendlich
CEILING Runden nach positiv unendlich
HALF_DOWN Runden zum nächsten Nachbarn und weg von der 0, wenn beide Nachbarn gleich weit weg sind
HALF_UP Runden zum nächsten Nachbarn und hin zur 0, wenn beide Nachbarn gleich weit weg sind
HALF_EVEN Runden zum nächsten Nachbarn und zum geraden Nachbarn, wenn beide Nachbarn gleich weit weg sind
UNNECESSARY Kein Runden, Operation muss exakt sein

 

ROUND_UNNECESSARY darf nur dann verwendet werden, wenn die Division exakt ist, sonst gibt es eine ArithmeticException.

BigDecimal one   = BigDecimal.ONE;

BigDecimal three = new BigDecimal( "3" );

System.out.println( one.divide( three, RoundingMode.UP ) );      // 1

System.out.println( one.divide( three, RoundingMode.DOWN ) );    // 0

Jetzt kann noch die Anzahl der Nachkommastellen bestimmt werden:

System.out.println( one.divide( three, 6, RoundingMode.UP ) );   // 0.333334

System.out.println( one.divide( three, 6, RoundingMode.DOWN ) ); // 0.333333

Beispiel

BigDecimal bietet die praktische Methode setScale(…) an, mit der sich die Anzahl der Nachkommastellen setzen lässt. Das ist zum Runden sehr gut. In unserem Beispiel sollen 45 Liter Benzin zu 1,399 bezahlt werden:

BigDecimal petrol = new BigDecimal( "1.399" ).multiply( new BigDecimal(45) );
 System.out.println( petrol.setScale( 3, BigDecimal.ROUND_HALF_UP ) );
 System.out.println( petrol.setScale( 2, BigDecimal.ROUND_HALF_UP ) );

Die Ausgaben sind 62955 und 6296.

class java.math.BigDecimal
extends Number
implements Comparable<BigDecimal>

  • BigDecimal divide(BigDecimal divisor,RoundingMode roundingMode)
  • BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
  • BigDecimal setScale(int newScale,RoundingMode roundingMode)