Ströme in Java
Schnittstellen mit Typparameter, Zuweisung an Variable
Zuweisung an eine Variable, deren Typ eine funktionale Schnittstelle ist
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ /* a.util.concurrent.Callable< java.lang.Double >callable = () -> 123; // nicht konvertierbar */
/* a.util.concurrent.Callable< java.lang.Double >callable1 =( () -> 123 ); // nicht konvertierbar */
java.util.concurrent.Callable< java.lang.Double >callable = () -> 123.0;
java.util.concurrent.Callable< java.lang.Double >callable1 =( () -> 123.0 );try
{ java.lang.System.out.println( callable.call() );
java.lang.System.out.println( callable1.call() );
java.lang.System.out.println( ( callable ).call() );
java.lang.System.out.println( ( callable1 ).call() ); }
catch( final java.lang.Exception exception )
{ java.lang.System.err.println( exception ); }
/* a.lang.System.out.println( callable() ); // nicht moeglich */ }}transcript
123.0
123.0
123.0
123.0
Folgende Programmbeispiel erzeugt ein Objekt der Schnittstelle Supplier.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final java.util.function.Supplier< java.lang.String >stringSupplier = "abc"::toString;
java.lang.System.out.println( stringSupplier );
java.lang.System.out.println( stringSupplier.get() );
/* a.lang.System.out.println( stringSupplier() ); // nicht moeglich */
/* a.lang.System.out.println( ( "abc"::toString ).get() ); // nicht moeglich */ }}transcript
Main$$Lambda$1/518248@1ee733d
abc
Mehr zu asList, Methoden mit Typparameter
Eine Liste, die mit asList erhalten wurde, basiert auf einer Reihung. Sie kann daher nicht mit add erweitert werden.
asList ist eine Methode mit einem Typparameter. Das zuvor gezeigte java.util.concurrent.Callable war hingegen eine Schnittstelle mit einem Typparameter.
static< T >List< T >asList( T ... a )
Der Typ T wird bei Aufruf automatisch angepaßt. Er kann auch vom Kontext abhängen:
Main.java
public final class Main
{ public static void main( final java.lang.String args[] ) throws java.lang.Throwable
{ /* a.util.List< java.lang.Double >r =( java.util.Arrays.asList( 2, 3.0 ) ); // nicht moeglich */
java.util.List< java.lang.Number >s =( java.util.Arrays.asList( 2, 3.0 ) );
java.util.List< java.lang.Object >t =( java.util.Arrays.asList( 2, 3.0 ) ); }}transcript
- .
Ein Typargument kann zur Laufzeit oft nicht ermittelt werden, da es nur während der Übersetzung verwendet wird (type erasure):
Main.java
public final class Main
{ public static void main( final java.lang.String args[] ) throws java.lang.Throwable
{ java.util.List< java.lang.Number >s =( java.util.Arrays.asList( 2, 3.0 ) );
java.util.List< java.lang.Object >t =( java.util.Arrays.asList( 2, 3.0 ) );
java.lang.System.out.println( s.getClass() );
java.lang.System.out.println( t.getClass() ); }}transcript
class java.util.Arrays$ArrayList
class java.util.Arrays$ArrayList
Ein Typargument einer Methode mit einem Typparameter kann auch ausdrücklich angegeben werden:
Main.java
public final class Main
{ public static void main( final java.lang.String args[] ) throws java.lang.Throwable
{ java.util.List< java.lang.Number >s = java.util.Arrays.< java.lang.Number >asList( 2, 3.0 );
java.util.List< java.lang.Object >t = java.util.Arrays.< java.lang.Object >asList( 2, 3.0 ); }}
Die ausdrückliche Angabe eines Typarguments ist nötig, wenn der Compiler keinen passenden Typ findet.
Ströme
asList: varargs-Methode
Main.java
public final class Main
{ public static void main( final java.lang.String args[] )
{ final java.util.List< java.lang.Integer >list = java.util.Arrays.asList( 1, 2, 3 );
java.lang.System.out.println( list.stream().reduce( 0,( x, y )-> x + y )); }}transcript
6
Main.java
public final class Main
{ public static void main( final java.lang.String args[] )
{ final java.util.List< java.math.BigDecimal >list
= java.util.Arrays.asList
( new java.math.BigDecimal( "3" ),
new java.math.BigDecimal( "4" ),
new java.math.BigDecimal( "5" ) );
java.lang.System.out.println
( list.stream().reduce
( new java.math.BigDecimal( "0" ),
java.math.BigDecimal::add )); }}transcript
12
Falls in einer inneren Methode bei der Verarbeitung eines Stromes ein Laufzeitfehler auftritt, kann der Stapelbericht dazu für einen Programmierer unübersichtlich sein.
Lotto ohne veränderliche Variablen oder Schleifen
6 aus 49
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{final int total_size = 49;
final int selection_size = 6;final java.util.List<java.lang.Integer> numbers =
java.util.stream.IntStream.rangeClosed( 1, total_size ).boxed().
collect( java.util.stream.Collectors.toList() );
java.util.Collections.shuffle( numbers );
java.lang.System.out.println( java.util.Arrays.toString( numbers.subList( 0, selection_size ).toArray() )); }}- Protokoll
[31, 11, 15, 40, 47, 44]
transcript
[46, 22, 25, 31, 26, 32]
transcript
[5, 20, 24, 2, 15, 14]
- Aussprachehinweis
- closed clozd
Vokalgrapheme zählen
public final class Main
{ public static void main( final java.lang.String args[] )
{ java.lang.System.out.println
( "aba".codePoints().
filter( x -> "aeiou".indexOf( x )>= 0 ).
count() ); }}
Vorteil: filter geht universell für alle möglichen Ströme (egal, ob diese aus einem Text oder einer Liste kommen), man muß nicht jeweils spezielle Methoden erlernen, einmal für String und einmal für Liste.
Parallele Ströme
Die parallelen Ströme sind die Motivation für die Einführung von Methodenausdrücken in Java!
- Man wollte einfachere Parallelität, um Rechner mit mehreren Kernen besser ausnutzen zu können.
- Parallele Ströme ermöglichen dies.
- Aber parallele Ströme (wie im folgenden Beispiel) wären ohne Methodenliterale mühevoll zu schreiben.
Java wird Operationen auf einem Strom, der nicht ausdrücklich als parallel gekennzeichnet ist, nicht von sich aus parallelisieren. Stromoperationen werden nur parallelisiert, wenn der Strom mit »parallelStream()« oder »stream().parallel()« oder auf eine gleichwertige Weise erzeugt wurde.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final java.util.List< java.lang.Integer >list = java.util.Arrays.asList( 1, 2, 3 );
java.lang.System.out.println( list.parallelStream().map( x -> x * x ).reduce( 0,( x, y )-> x + y )); }}transcript
14
Die Parallelisierung eines Stroms kann Programme in bestimmten Fällen auch verlangsamen (etwa, wenn es schon viele Threads gibt).
Es reicht nicht immer, einfach nur »stream« durch »parallelStream« zu ersetzen, manchmal muß man auch noch andere Operationen an die Parallelverarbeitung anpassen:
public final class Main
{ public static void main( final java.lang.String[] args )
{ java.util.Arrays.asList( "E", "J", "B", "B", "D", "H", "" ).
parallelStream().
filter( x -> !x.isEmpty() ).
distinct().
sorted().
forEachOrdered( java.lang.System.out::println ); }}
Die Verwendung von »forEachOrdered« statt »forEach« führt zum gewünschten Verhalten, aber kann auch dafür sorgen, daß keine Parallelverarbeitung stattfindet.
forEach
Main.java
public final class Main
{
public static void p( final java.lang.String text )
{ java.lang.System.out.println( text ); }
public static void trg( final java.lang.String wort )
{ p( "Tomaten" ); p( "Rotkohl Gruenkohl" ); p( wort ); }
public static void main( final java.lang.String[] args )
{ trg( "Gurken" ); trg( "Spinat" ); trg( "Kohlrabi" ); }}transcript
Tomaten
Rotkohl Gruenkohl
Gurken
Tomaten
Rotkohl Gruenkohl
Spinat
Tomaten
Rotkohl Gruenkohl
KohlrabiMain.java
public final class Main
{
public static void p( final java.lang.String text )
{ java.lang.System.out.println( text ); }
public static void trg( final java.lang.String wort )
{ java.util.Arrays.asList( "Tomaten", "Rotkohl Gruenkohl", wort ).forEach( Main::p ); }
public static void main( final java.lang.String[] args )
{ java.util.Arrays.asList( "Gurken", "Spinat", "Kohlrabi" ).forEach( Main::trg ); }}transcript
Tomaten
Rotkohl Gruenkohl
Gurken
Tomaten
Rotkohl Gruenkohl
Spinat
Tomaten
Rotkohl Gruenkohl
Kohlrabi
Der Compiler schließt auf den Typ der Liste und wählt so die passende Überladung von println.
Main.java
public final class Main
{
public static void main( final java.lang.String[] args )
{ java.util.Arrays.asList( "hello, ", "world" ).
forEach( java.lang.System.out::println ); }}transcript
hello,
world
In dem folgenden Beispiel wäre auch java.lang.System.out::println möglich
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
throws java.lang.Exception
{ java.util.Arrays.asList
( new java.io.File( "." ).getCanonicalPath(),
new java.io.File( "" ).getCanonicalPath(),
java.lang.System.getProperty( "user.dir" ),
java.nio.file.Paths.get( "" ).toAbsolutePath().normalize() )
.forEach( cCorrente -> java.lang.System.out.println( cCorrente )); }}transcript
C:\
C:\
C:\
C:\
? Typen (1)
Welchen Typ hat der Ausdruck »java.lang.System.out.getClass()«?
? Typen (2)
Welchen Typ hat das Objekt »java.lang.System.out.getClass()«?
? Typen (3) *
Können Sie die Ausgabe des folgenden Programms voraussagen, ohne es zu starten?
Main.java
public final class Main
{ final public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( "Alpha".getClass().getClass() );
java.lang.System.out.println( "Alpha".getClass().toString().getClass() ); }}- Main.java
public final class Main
{
public static boolean identbody( final int c )
{ return c != 'Z' && java.lang.Character.isJavaIdentifierPart( c ); }
public static void println
( final java.util.function.Function<Integer,Boolean> pred )
{ java.util.stream.IntStream.rangeClosed( 32, 126 ).
filter( pred::apply ).
forEach( c -> java.lang.System.out.print(( char )c ));
java.lang.System.out.println(); }
public static void main( final java.lang.String[] args )
{ println( java.lang.Character::isJavaIdentifierStart );
println( Main::identbody ); }}
transcript
$ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz
$0123456789ABCDEFGHIJKLMNOPQRSTUVWXY_abcdefghijklmnopqrstuvwxyz