Advanced   Java   Services StringStreams Back Next Up Home


Stream<String>


Erzeugen von Stream<String>

of()-Methode
Stream<String> stringStream = Stream.of("eins", "zweins", "dreins", "vierns");

List<String> -> Stream<String>
List<String> stringList = Arrays.asList("eins", "zweins", "dreins", "vierns");
Stream<String> stringStream = stringList.stream();
Stream<String> parStream = stringList.parallelStream();

Set<String> -> Stream<String>
Set<String>  stringSet = new HashSet<>(stringList);
Stream<String> stringStream = stringSet.stream();
Stream<String> parStream = stringSet.parallelStream();

String[] -> Stream<String>
String[] stringArr = {"eins", "zweins", "dreins", "vierns"};
Stream<String> stringStream3 = Arrays.stream(stringArr);

Aus einer Textdatei mit BufferedReader
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);
}

Aus einer Textdatei mit Files.lines
// 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);
}

Einige Methoden

allMatch() anyMatch()

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);

distinct(), map(), sorted(), forEach()

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

concat()

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

Streams in Listen, Sets oder Arrays überführen

Hier hilft die Hilfsklasse Collectors, die für die Methode collect() fertige Collectoren zure Verfügung stellt.


Stream<String>  ->  List<String>

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>  ->  Set<String>

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!

Stream<String>  ->  String[]

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]);

Stream<T>  ->  Map<K,V>

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.


static <T,K,V> Collector<T,?,Map<K,V>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends V> valueMapper)

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}


static <T,K,V> Collector<T,?,Map<K,V>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends V> valueMapper, BinaryOperator<V> mergeFunction)

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}



static <T,K,U,M extends Map<K,U>> Collector<T,?,M> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)

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}

Weitere Methoden

generate() , iterate()
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





flatMap()
<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);


















































Valid XHTML 1.0 Strict top Back Next Up Home