Advanced Java Services | StringStreams |
Stream<String> stringStream = Stream.of("eins", "zweins", "dreins", "vierns");
List<String> stringList = Arrays.asList("eins", "zweins", "dreins", "vierns"); Stream<String> stringStream = stringList.stream(); Stream<String> parStream = stringList.parallelStream();
Set<String> stringSet = new HashSet<>(stringList); Stream<String> stringStream = stringSet.stream(); Stream<String> parStream = stringSet.parallelStream();
String[] stringArr = {"eins", "zweins", "dreins", "vierns"}; Stream<String> stringStream3 = Arrays.stream(stringArr);
try(BufferedReader br = new BufferedReader( new FileReader("einstein.txt"));) { Stream<String> lines = br.lines(); lines.forEach(System.out::println); } catch(FileNotFoundException ex) { throw new UncheckedIOException(ex); } catch(IOException ex) { throw new UncheckedIOException(ex); }
// Files.lines Achtung: lines hat zwei Parameter try (Stream<String> lines = Files.lines(Paths.get("einstein.txt"), Charset.defaultCharset() ) ) { lines.forEach(System.out::println); } catch(FileNotFoundException ex) { throw new UncheckedIOException(ex); } catch(IOException ex) { throw new UncheckedIOException(ex); }
Beide Methoden erwarten ein Predicate: Predicate<? super String> predicate .
Stream<String> stringStream = Stream.of("Chris", "Dora", "Bernhard", "Beatrice", "Erich", "Fritz", "Brigitte", "Esther", "Andrea");
boolean allUpper = stringStream.allMatch( s -> Character.isUpperCase(s.charAt(0)) ); System.out.println(allUpper);
Stream<String> stringStream2 = Stream.of("Chris", "Dora", "Bernhard", "Beatrice", "Erich", "Fritz", "Brigitte", "Esther", "Andrea"); boolean greater4 = stringStream2.allMatch( s -> s.length() > 4 ); System.out.println(greater4);
Stream<String> stringStream3 = Stream.of("Chris", "Dora", "Bernhard", "Beatrice", "Erich", "Fritz", "Brigitte", "Esther", "Andrea"); boolean length4 = stringStream3.anyMatch( s -> s.length() == 4 ); System.out.println(length4);
Stream<String> stringStream4 = Stream.of("Chris", "Dora", "Bernhard", "Beatrice", "Erich", "Fritz", "Brigitte", "Esther", "Andrea"); boolean smaller4 = stringStream4.anyMatch( s -> s.length() < 4 ); System.out.println(smaller4);
Consumer<String> printString = s -> System.out.print(s + " "); Stream<String> stringStream = Stream.of("c", "d", "b", "b", "e", "f", "b", "c", "a"); stringStream.distinct().sorted().map( s -> s.toUpperCase() ).forEach(printString);
Ausgabe
A B C D E F
Mit dieser Methode kann man zwei Streams aneinanderhängen.
Consumer<CharSequence> printChars = s -> System.out.print(s + " "); Stream<String> stream1 = Stream.of("a", "b", "e", "f", "g"); Stream<String> stream2 = Stream.of("g", "e", "d", "c", "a"); Stream<String> glued = Stream.concat(stream2, stream1); glued.distinct().sorted( Comparator.reverseOrder() ).forEach(printChars);
Ausgabe
g f e d c b a
Hier hilft die Hilfsklasse Collectors, die für die Methode collect() fertige Collectoren zure Verfügung stellt.
Stream<String> stream1 = Stream.of("g", "b", "e", "f", "g", "d", "e", "c", "b", "a"); List<String> stringList = stream1.sorted().collect(Collectors.toList()); System.out.println(stringList); //[a, b, b, c, d, e, e, f, g, g]
Stream<String> stream2 = Stream.of("g", "b", "e", "f", "g", "d", "e", "c", "b", "a"); Set<String> stringSet = stream2.collect(Collectors.toSet()); System.out.println(stringSet); // [a, b, c, d, e, f, g] sortiert!
Es gibt zwei toArray()-Methoden im Interface Stream. Die erste liefert nur ein Array vom Typ Object.
Stream<String> stream3 = Stream.of("g", "b", "e", "f", "g", "d", "e", "c", "b", "a"); Object[] obArr = stream3.toArray();
Die zweite aber ein Array des Typ des Streams.
Stream<String> stream4 = Stream.of("g", "b", "e", "f", "g", "d", "e", "c", "b", "a"); String[] stringArr = stream4.toArray(String[]::new); // ausführlich: String[] stringArr = stream4.toArray( (size) -> new String[size]);
Hierzu verwenden wir eine Klasse Person in einfacher Form
public class Person { private String name; private String phone; private Gender gender; public Person(String name, String phone, Gender gender) { this.name = name; this.phone = phone; this.gender = gender; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public Gender getGender() { return gender; } public void setGender(Gender gender) { this.gender = gender; } @Override public String toString() { return "(" + name + "," + phone + "," + gender +")"; } }
Wir brauchen noch ein einfaches Enum:
enum Gender { MALE, FEMALE ; }
Es gibt drei verschiedene toMap()-Methoden im Interface Collectors, eine mit zwei Paraemetern, eine mit drei parametern und eine mit vier Parametern.
Hier haben wir nur zwei Parameter.
Wir wollen eine Zuordnung zwischen Telefonnummern und Namen, also einen Stream von Personen überführen in ein Map wie folgt.
Stream<Person> ->
Map<String,String>
Dafür gibt es in der Hilfsklasse Collectors die Methode toMap(), die den folgenden Aufbau hat:
static <T,K,V> Collector<T,?,Map<K,V>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends V> valueMapper)
Die Funktionen in den Parameter müssen aus den Personen Key und Value rausfiltern, es ist also
T = Person , K = String , V = String
und damit sieht toMap() nun folgendermaßen aus:
static <Person,String,String> Collector<Person,?,Map<String,String>> toMap(Function<? super Person,? extends String> keyMapper, Function<? super Person,? extends String> valueMapper)
Person[] persArr = { new Person("maria", "1234", Gender.female), new Person("karl", "4567", Gender.male), new Person("esther", "2084", Gender.female), new Person("brigitte", "3579", Gender.female) }; Stream<Person> persStream = Arrays.stream(persArr); Function<? super Person,? extends String> keyMapper = Person::getPhone; Function<? super Person,? extends String> valueMapper = Person::getName; Map<String, String> map = persStream.collect(Collectors.toMap(keyMapper, valueMapper)); System.out.println(map); //{3579=brigitte, 1234=maria, 4567=karl, 2084=esther}
Hier haben wir zusätzlich als dritten Parameter BinaryOperator<V> mergeFunction
Wir wollen als Key das Geschlecht und als Value alle Namen, die zu diesem Geschlecht gehören, also
T = Person , K = Gender , V = String
Die ersten beiden Parameter sind völlig analog, mit dem dritten Parameter müssen wir addieren:
Function<? super Person,? extends Gender> keyMapper = Person::getGender; Function<? super Person,? extends String> valueMapper = Person::getName; BinaryOperator<String> mergeFunction = (v1, v2) -> String.join(" + ", v1, v2);
Das ganze Beispiel
Person[] persArr = { new Person("maria", "1234", Gender.FEMALE), new Person("karl", "4567", Gender.MALE), new Person("hans", "4567", Gender.MALE), new Person("esther", "2084", Gender.FEMALE), new Person("brigitte", "3579", Gender.FEMALE) }; Stream<Person> persStream = Arrays.stream(persArr); Function<? super Person,? extends Gender> keyMapper = Person::getGender; Function<? super Person,? extends String> valueMapper = Person::getName; BinaryOperator<String> mergeFunction = (v1, v2) -> String.join(" + ", v1, v2); Map<Gender, String> gender2NamesMap = persStream.collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction)); System.out.println(gender2NamesMap); //{male=karl + hans, female=maria + esther + brigitte}
Mit dem vierten Parameter kann man eine bestimmte Map vorgeben, wir wollen eine TreeMap und eine Sortierung nach dem Geschlecht. Sortierung nach den Keys
Person[] persArr = { new Person("maria", "1234", Gender.FEMALE), new Person("karl", "4567", Gender.MALE), new Person("hans", "4567", Gender.MALE), new Person("esther", "2084", Gender.FEMALE), new Person("brigitte", "3579", Gender.FEMALE) }; Stream<Person> persStream = Arrays.stream(persArr); Function<? super Person,? extends Gender> keyMapper = Person::getGender; Function<? super Person,? extends String> valueMapper = Person::getName; BinaryOperator<String> mergeFunction = (v1, v2) -> String.join(" + ", v1, v2); Comparator<Gender> genderComparator = (g1, g2) -> g1.toString().compareTo(g2.toString()); Supplier<Map<Gender,String>> mapSupplier = () -> new TreeMap(genderComparator); Map<Gender, String> gender2NamesMap = persStream.collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)); System.out.println(gender2NamesMap); //{female=maria + esther + brigitte, male=karl + hans}
Supplier<String> supp = () -> "a" + (1 + (int)Math.floor(Math.random()*9)) ; Stream.generate(supp).limit(10).distinct().forEach(System.out::println);
Ausgabe
a5 a8 a4 a1 a3 a6 a9
Stream.iterate("a", i -> i+"x").limit(5).forEach(System.out::println);
Ausgabe
a ax axx axxx axxxx
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
Mit dieser Methode kann man mehrere Streams zu einem Stream "verflachen". Im folgenden Beispiel werden drei Listen verschiedenen Typs angelegt. Daraus wird ein Stream von drei Listen erstellt. Aus diesem Stream wird dann ein Stream erstellt der alle Elemenmte dieser drei Listen enthält. Diesen Stream kann man dann leicht in eine Liste verwandeln.
List<String> strList = Arrays.asList("eins", "zweins", "dreins"); List<StringBuilder> stribuList = Arrays.asList(new StringBuilder("zweins"), new StringBuilder( "dreins"), new StringBuilder("vierns")); List<CharBuffer> charBuffList = Arrays.asList(CharBuffer.wrap("dreins".toCharArray()), CharBuffer.wrap("vierns".toCharArray()), CharBuffer.wrap("fünfs".toCharArray())); // Ein Stream von 3 Listem Stream<List<? extends CharSequence>> streamOfLists = Stream.of(strList, stribuList, charBuffList); //streamOfLists.forEach(printObject); //[eins, zweins, dreins] [zweins, dreins, vierns] [dreins, vierns, fünfs] // <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper) // T = Eingangstyp = List<? extends CharSequence> // Returntyp = Stream<R> = Stream<? extends CharSequence> Function<? super List<? extends CharSequence>, Stream<? extends CharSequence>> flattener = list -> list.stream(); // ein Stream aus den Inhalten der drei Listen List<CharSequence> list = streamOfLists.flatMap(flattener).collect(Collectors.toList()); System.out.println(list);