Advanced Java Services | IntStreams |
Im folgenden werden eine Reihe von Beispielen für IntStreams zusammengestellt.
IntStream.range(0, 12).forEach(System.out::println); // 0 1 2 3 4 5 6 7 8 9 10 11 //Endwert ist nicht dabei
IntStream.rangeClosed(0, 12).forEach(System.out::println); // 0 1 2 3 4 5 6 7 8 9 10 11 12 //Endwert ist dabei
// aus einer Folge von Literalen IntStream.of(0, 1, 1, 2, 4, 7, 13, 24, 44, 81, 149, 274 ).forEach(System.out::println);
// aus einem Array mit Arrays.stream int[] intArr = { 1, 1, 2, 3, 5, 8, 13}; Arrays.stream(intArr).forEach(System.out::println);
generate() erzeugt einen quasi endlosen Stream, der mit limit() beschränkt wird.
IntSupplier supp = () -> (int)Math.floor(Math.random()*100) ; IntStream.generate(supp).limit(10).forEach(System.out::println);
iterate() erzeugt einen quasi endlosen Stream, der mit limit() beschränkt wird.
// Erzeugen von Potenzen von 2 IntStream.iterate(1, i -> 2*i).limit(11).forEach(System.out::println);
// Erzeugen ungerder Zahlen IntStream.iterate(1, i -> i+2).limit(6).forEach(System.out::println);
int[] intArr = { 1, 1, 2, 3, 5, 8, 13}; OptionalDouble od = Arrays.stream(intArr).average(); if (od.isPresent()) System.out.println(od.getAsDouble()); long count = Arrays.stream(intArr).count(); System.out.println(count); OptionalInt oiMax = Arrays.stream(intArr).max(); if(oiMax.isPresent()) System.out.println(oiMax.getAsInt()); OptionalInt oiMin = Arrays.stream(intArr).min(); if(oiMin.isPresent()) System.out.println(oiMin.getAsInt()); int sum = Arrays.stream(intArr).sum(); System.out.println(sum);
IntStream intStream1 = IntStream.of(0, 2, 4, 6, 8); IntStream intStream2 = IntStream.of(1, 3, 5, 7, 9); IntStream concat = IntStream.concat(intStream1, intStream2); concat.forEach(System.out::println);
IntStream fibo2 = Arrays.stream(intArr); OptionalInt oi2 = fibo2.findFirst(); System.out.println("first = " + oi2.getAsInt());
int[] intArr = {1, 2, 3, 5, 8, 13, 21, 34, 55 }; IntStream fibo = Arrays.stream(intArr); OptionalInt oi = fibo.findAny(); System.out.println("any = " + oi.getAsInt()); // liefert meistens auch das erste Element
IntStream digits = IntStream.range(1, 10); digits.map( i -> i*i*i).forEach(System.out::println); System.out.println(); IntStream netto = IntStream.of(123, 456, 789, 234, 567 ); DoubleStream doubleStream = netto.mapToDouble( i -> 0.19*i ); doubleStream.forEach(System.out::println);
Ausgabe
1 8 27 64 125 216 343 512 729 23.37 86.64 149.91 44.46 107.73
forEachOrdered stellt bei parallelen Streams die ursprüngliche Reihenfolge wieder her.
IntConsumer printInt = i -> System.out.print(i + " "); IntStream digits = IntStream.range(0, 10); digits.map( i -> i*i).forEach(printInt); System.out.println(); IntStream digits2 = IntStream.range(0, 10); digits2.parallel().map( i ->i*i).forEach(printInt); System.out.println(); IntStream digits3 = IntStream.range(0, 10); digits3.parallel().map( i -> i*i).forEachOrdered(printInt); System.out.println();
Ausgabe
0 1 4 9 16 25 36 49 64 81 36 25 4 49 16 64 9 81 1 0 0 1 4 9 16 25 36 49 64 81
Straightforward...
IntSupplier supp = () -> (int)Math.floor(Math.random()*100) ; IntStream.generate(supp).limit(16).distinct().filter( i -> i%2 == 1).sorted().forEach(printInt); // 21 33 41 57 59 77 79 83 87 // 9 35 41 47 83 91
peek() ist u.a. dazu verwendet werden um eine Kontrollausgabe zu machen um den Zustand eines Streams auszugeben.
IntSupplier supp = () -> (int)Math.floor(Math.random()*100) ; IntStream.generate(supp).limit(12) .peek( i -> System.out.print("("+i+")")) .distinct() .filter( i -> i%2 == 1) .sorted() .forEach(i -> System.out.print("["+i+"]")); // (88)(89)(91)(82)(26)(52)(5)(95)(66)(83)(19)(65)[5][19][65][83][89][91][95] // (46)(11)(11)(8)(53)(99)(48)(59)(18)(84)(54)(18)[11][53][59][99]
Der IntBinaryOperator wird wiederholt aufgerufen, wie das folgende Beispiel zeigt
IntStream intStream = IntStream.rangeClosed(1, 10); // In i entsteht die "Summe", die am Ende zurückgegeben wird // j durchwandert die Streamelemente OptionalInt oi = intStream.reduce( (i,j) -> { System.out.println("i="+i+" j="+j); return i+2*j; } ); if(oi.isPresent()) System.out.println(oi.getAsInt());
Ausgabe
i=1 j=2 i=5 j=3 i=11 j=4 i=19 j=5 i=29 j=6 i=41 j=7 i=55 j=8 i=71 j=9 i=89 j=10 109
In der zweiten Fassung wird der erste Parameter als Startwert verwendet. Der IntBinaryOperator wird wiederholt aufgerufen, wie das folgende Beispiel zeigt
IntStream intStream = IntStream.rangeClosed(1, 10); // in i entsteht das Ergebnis, das am Ende zurückgegeben wird int erg = intStream.reduce(0, (i,j) -> { System.out.println("i="+i+" j="+j); return i+2*j; } ); System.out.println(erg);
Ausgabe
i=0 j=1 i=2 j=2 i=6 j=3 i=12 j=4 i=20 j=5 i=30 j=6 i=42 j=7 i=56 j=8 i=72 j=9 i=90 j=10 110
Das Beispiel erzeugt aus einem IntStream einen zweiten, indem jedes Element aus dem ersten im zweiten Stream dreinal vorhanden ist.
IntConsumer printInt = i -> System.out.print(i + " "); IntFunction<? extends IntStream> mapper = i -> IntStream.iterate(i, k -> k).limit(3) ; IntStream sequence = IntStream.rangeClosed(1, 5).flatMap(mapper); sequence.forEach(printInt);
Ausgabe
1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
Bei collect() ist es wichtig sich klarzumachen, daß R der Typ ist der zurückgegeben wird. Bei vielen Anwendungen wird der dritte Parameter von collect() nicht verwendet, in diesen Fällen kann einfach null übergeben werden.
<R> R collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R,R> combiner)
Prinzip
Der Returntyp R fungiert als ein Art Behälter, in dem die Elemente des Streams landen. Dieser Behälter kann etwa eine Liste sein, die alle Elemente oder ausgewählte Elemente des Streams aufnimmt. Der Behälter kann aber auch nur ein einziges Objekt oder auch ein primitiver Datentyp sein, etwa ein long, das die Summe aller Elemente des Intstreams aufnimmt. Aufgabe des accumulator ist es, die Elemente des Streams in diesen Behälter zu bringen. Der supplier dagegen muß den Behälter bereitstellen. Bei parallelen Streams müssen die beiden Streams wieder zusammengführt werden, diese Aufgabe übernimmt dann der combiner.
Wir bringen eine Reihe von Fibonacci-Zahlen in eine Liste.
IntStream intStream = IntStream.of(0, 1, 1, 2, 3, 5, 8, 13, 21, 34); // Parameter 1 von collect: Supplier<R> : erzeugt ein Behälterobjekt, hier eine Liste Supplier<List<Integer>> supp = ArrayList::new; // Parameter 2 von collect: ObjIntConsumer<R> : // Der ObjIntConsumer bekommt als ersten Parameter das durch den Supplier erzeugte Objekt // und als zweiten Parameter das zu akkumulierende Objekt aus dem Stream // hier: erster Parameter die Liste, zweiter Parameter ein int aus dem Stream // Vorgang: int in die Liste aufnehmen ObjIntConsumer<List<Integer>> oic = List::add; // void accept(T t, int value) // Parameter 3 : BiConsumer<R,R> : void accept(R t, R u) // wird hier bei einem NICHT-parallelen streams nicht verwendet, kann dann null sein List<Integer> intList = intStream.collect(supp, oic, null); System.out.println(intList);
Ausgabe
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Wir bringen eine Reihe von Fibonacci-Zahlen in eine Liste.
IntStream intStream = IntStream.of(0, 1, 1, 2, 3, 5, 8, 13, 21, 34); // Parameter 1 von collect: Supplier<R> : erzeugt ein Behälterobjekt, hier eine Liste Supplier<List<Integer>> supp = ArrayList::new; // Parameter 2 von collect: ObjIntConsumer<R> : // Der ObjIntConsumer bekommt als ersten Parameter das durch den Supplier erzeugte Objekt // und als zweiten Parameter das zu akkumulierende Objekt aus dem Stream // hier: erster Parameter die Liste, zweiter Parameter ein int aus dem Stream // Vorgang: int in die Liste aufnehmen ObjIntConsumer<List<Integer>> oic = List::add; // void accept(T t, int value) // paralleler Fall //////////////////////////////////////////// // Parameter 3 : BiConsumer<R,R> : void accept(R a, R b) // bekommt als ersten Parameter das vom Supplier erzeugte Objekt // und als zweiten Parameter ein temporäres Behältertobjekt desselben Typs // Vorgang: Inhalt des temporären Objekts in das zum ersten Parameter gehörende Objekt bringen // wird nur im parallen Fall gebraucht, // Um zu sehen wie die zurückgegebene Liste aufgebaut wird gibt es hier ein "syso". BiConsumer<List<Integer>,List<Integer>> bicons = (a,b) -> { System.out.println( "a = " + a + " b = " + b); a.addAll(b); } ; // Vorsicht: Es kommt auf die Reihenfolge an: a ist die Liste, die zurückgegeben wird // kürzer, aber undurchsichtiger: BiConsumer<List<Integer>,List<Integer>> bicons = List::addAll ; List<Integer> intList = intStream.parallel().collect(supp, oic, bicons); System.out.println(intList);
Ausgabe
a = [5] b = [8] a = [21] b = [34] a = [13] b = [21, 34] a = [5, 8] b = [13, 21, 34] a = [2] b = [3] a = [1] b = [2, 3] a = [0] b = [1] a = [0, 1] b = [1, 2, 3] a = [0, 1, 1, 2, 3] b = [5, 8, 13, 21, 34] [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Mit der Factoryklasse Collectors kann man das Vorgehen deutlich vereinfachen. Diese Hilfsklasse stellt durch statische Methoden fertife Collectoren bereit, die für viele Anwendungen ausreichend sind. Die folgende Beispiele zeigen, wie man einen IntStream in eine Liste oder eine Set verwandeln kann.
IntStream intStream = IntStream.of(0, 1, 1, 2, 3, 5, 8, 13, 21, 34); List<Integer> intList = intStream.boxed().collect( Collectors.toList());
oder auch
IntStream intStream2 = IntStream.of(0, 1, 1, 2, 3, 5, 8, 13, 21, 34); List<Integer> intList = intStream.mapToObj(Integer::new).collect(Collectors.toList());
IntStream intStream = IntStream.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4); Set<Integer> intSet = intStream.boxed().collect( Collectors.toSet());
oder auch
IntStream intStream = IntStream.of(0, 0, 1, 1, 2, 2, 3, 3, 4, 4); Set<Integer> intSet = intStream.mapToObj(Integer::new).collect(Collectors.toSet());