Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Leer Stream-API in Java | Geavanceerde Java-Functies en -Technieken
Java Datastructuren

bookStream-API in Java

Er zijn verschillende manieren om gegevens te verwerken in Java – lussen, methoden en diverse algoritmen. Echter, in Java 8 werd een zeer krachtig hulpmiddel geïntroduceerd – de Stream API.

Eenvoudig gezegd is de Stream API een manier om snel en eenvoudig te werken met een stroom van informatie. In dit geval wordt deze informatiestroom weergegeven door collecties. De Stream API kent enkele concepten. Hier zijn de belangrijkste.

Belangrijkste concepten

  • Stream: vertegenwoordigt een reeks gegevens­elementen die verwerkt kunnen worden;

  • Intermediate operations: bewerkingen die na uitvoering een nieuwe stream creëren. Voorbeelden: filter, map, distinct, sorted;

  • Terminal operations: bewerkingen die de verwerking van de stream afronden en een resultaat teruggeven. Voorbeelden: collect, forEach, count, reduce;

  • Parallel streams: maken parallelle verwerking van gegevens mogelijk. Methoden parallel() en parallelStream() worden gebruikt om parallelle streams te creëren.

Genoeg theorie, laten we beginnen met coderen!

Een stream declareren gebeurt door een methode te gebruiken op de collectie die u wilt omzetten naar een stream:

Main.java

Main.java

copy
12
List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();

Met de stream()-methode verkrijgen we een stream van strings. Om echter met de stream te werken, is het noodzakelijk te begrijpen wat lambda-expressies zijn, aangezien stream-methoden voornamelijk hiermee werken.

Lambda-expressies

Lambda-expressies zijn geïntroduceerd in Java 8 en vormen een vereenvoudigde manier om anonieme functies te creëren in Java. We hebben anonieme functies tot nu toe niet behandeld omdat ze niet strikt noodzakelijk waren, maar nu maken we er kennis mee via lambda-expressies.

Syntax van lambda-expressies:

De algemene syntax voor lambda-expressies in Java ziet er als volgt uit:

Example.java

Example.java

copy
123
(parameters) -> expression // or (parameters) -> { statements; }
  • Parameters: dit is een parameterlijst die leeg kan zijn of één of meer parameters kan bevatten;

  • Pijl: weergegeven door het symbool ->, dat de parameters scheidt van het lichaam van de lambda-expressie;

  • Expressie of statements: dit is het functielichaam, dat een expressie of een blok statements bevat.

Hier is een voorbeeld van een lambda-expressie die een eenvoudige functie voorstelt die twee getallen optelt:

Example.java

Example.java

copy
12345678910
// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;

Laten we nader bekijken wat er precies gebeurt in de bovenstaande code en hoe we lambda-expressies gebruiken:

Main.java

Main.java

copy
1234567891011121314
package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }

In de bovenstaande code:

Een functionele interface MyMathOperation aangemaakt met één abstracte methode operate.

Een lambda-expressie gebruikt om deze methode te implementeren, waarbij de optelling van twee getallen wordt uitgevoerd.

Het resultaat van de optelling afgedrukt.

Het is begrijpelijk dat het moeilijk te begrijpen kan zijn wat er in deze code gebeurt, maar laten we teruggaan naar de Stream API, waar lambda-expressies vaak worden gebruikt, en proberen te begrijpen hoe deze in de praktijk worden toegepast.

Zoals je je herinnert, hebben we eerder een stream van strings gemaakt uit een lijst van strings. Laten we nu stream-methoden gebruiken om elke string in deze stream om te zetten naar hoofdletters:

Main.java

Main.java

copy
12345678910111213
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }

In de bovenstaande code is gebruikgemaakt van een lambda-expressie en twee methoden: map() en toList(). Indien duidelijk is wat de methode toList() doet, wijzigt de methode map() elk element in de stream volgens de opgegeven lambda-expressie.

Hieronder volgt een nadere toelichting op de werking van de lambda-expressie in dit geval:

De methode map() past de methode toUpperCase() toe op elk element van de stream. Het element van deze stream is gedefinieerd als e en met behulp van de lambda-expressie wordt het programma geïnstrueerd om deze methode op elk element toe te passen.

Dit is echter nog niet het einde, omdat hier sprake is van een intermediaire bewerking. Dit betekent dat bewerkingen op de stream nog niet zijn afgerond. Om het werk op de stream af te ronden, moet een terminale bewerking worden toegepast, waarmee de bewerkingen op de stream worden beëindigd en een specifieke waarde wordt geretourneerd. Bijvoorbeeld, de methode toList() kan worden gebruikt, waarna de gewijzigde stream wordt omgezet in een lijst.

Bijvoorbeeld:

Main.java

Main.java

copy
1234567891011121314
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }

Laten we de mogelijke intermediaire bewerkingen in de stream nader bekijken.

Intermediaire bewerkingen

De map()-methode – deze methode is reeds bekend; voert bewerkingen uit die door de lambda-expressie zijn gespecificeerd op elk element in de stream.

Bijvoorbeeld, gebruik de substring()-methode op elk element in de stream van strings:

Main.java

Main.java

copy
123456789101112131415
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }

De methode filter() accepteert een lambda-expressie met een voorwaarde waarop de stream wordt gefilterd. Met andere woorden, alle elementen die aan de voorwaarde voldoen zullen in de stream blijven, en elementen die niet aan de voorwaarde voldoen worden uit de stream verwijderd. Laten we de stream aanpassen zodat alleen de elementen waarvan de lengte groter is dan 5 behouden blijven:

Main.java

Main.java

copy
12345678910111213141516
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }

Met de methode filter() wordt de string "with" uit de stream verwijderd omdat dit woord minder dan 5 tekens bevat.

Het is ook mogelijk om meerdere tussenliggende bewerkingen achter elkaar te gebruiken.

Bijvoorbeeld, de bovenstaande code kan iets worden vereenvoudigd:

Main.java

Main.java

copy
12345678910111213141516
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }

Bij het combineren van meerdere stream-methoden wordt aanbevolen om elke methode op een nieuwe regel te plaatsen om de leesbaarheid van de code aanzienlijk te verbeteren.

De methode flatMap() transformeert elk element van een stream naar een nieuwe stream en combineert de resultaten tot één enkele stream. Met andere woorden, met deze methode kunnen we de stream opdelen in meerdere streams, die vervolgens worden samengevoegd tot één stream. Stel bijvoorbeeld dat je een lijst met strings hebt, waarbij elke string meer dan één woord kan bevatten, zoals een lijst met voor- en achternamen. En je moet de eerste letter van elk van deze woorden kapitaliseren:

Main.java

Main.java

copy
123456789101112131415161718192021222324
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }

In de bovenstaande code hebben we een aparte private methode geschreven die de eerste letter van een woord kapitaliseert en deze methode gebruikt in de map()-methode samen met een lambda-expressie.

Let op dat we met de flatMap-methode elk element van de stream opdelen in verschillende streams met behulp van de methode Arrays.stream(e.split(" ")). Omdat de split()-methode een array retourneert, moeten we de methode Arrays.stream() gebruiken om deze array op te splitsen in streams.

Daarna worden al deze streams samengevoegd tot één stream, waarna we de door ons geschreven methode gebruiken. Nu hebben we alle voor- en achternamen van gebruikers met een gekapitaliseerde eerste letter.

Weet je wat handig zou zijn? Als we deze voor- en achternamen in een HashMap plaatsen, waarbij de sleutel de achternaam is en de waarde de voornaam.

Laten we dit implementeren in de code:

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334
package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }

Met een eenvoudige lus hebben we de voornaam en achternaam in variabelen opgeslagen en vervolgens in de map geplaatst. Let op hoe de lus werkt. We verhogen de variabele i met 2 bij elke iteratie omdat we de achternaam moeten overslaan zodra deze al is vastgelegd.

  • De methode distinct() verwijdert duplicaten uit de stream. In het algemeen is dit nuttig als unieke elementen in de stream vereist zijn of als u snel duplicaten uit een lijst wilt verwijderen. Dit kan eenvoudig worden bereikt met de volgende constructie:
list.stream().distinct().toList()
  • De methode sorted sorteert alle elementen in de stream in natuurlijke volgorde, van het kleinste naar het grootste getal of in alfabetische volgorde. Dit is ook nuttig als een gesorteerde stream nodig is of als u snel een lijst wilt sorteren;

  • De methode skip(n) slaat de eerste n elementen van de stream over. Dit is nuttig bij het werken met tekstbestanden, waarbij de eerste n regels bijvoorbeeld metadata of een bestandsbeschrijving kunnen zijn. Het is ook vermeldenswaardig dat de methode limit(n) doorgaans het aantal elementen in de stream beperkt. Zelfs als een stream met 1000 elementen wordt gemaakt en vervolgens limit(200) wordt gebruikt, bevat de stream slechts de eerste 200 elementen.

Main.java

Main.java

copy
123456789101112
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }

Dit zijn de belangrijkste intermediaire methoden die u moet gebruiken. U kunt de overige methoden verkennen door te verwijzen naar de link naar de officiële Java-documentatie. Laten we doorgaan naar terminalmethoden.

Terminalmethoden

  • De terminalmethode waarmee u al bekend bent, is toList(). Deze zet de stream om in een lijst en retourneert deze. Met andere woorden, u kunt deze stream met methoden direct aan een lijst toewijzen. Deze methode is geïntroduceerd in Java 17 en dient als vervanging voor de meer complexe constructie collect(Collectors.toList());

  • De collect()-methode zet de stream ook om in een specifieke datastructuur. Als parameter gebruikt deze een methode uit de Collectors-interface. Deze interface bevat methoden zoals toList(), toSet() en toCollection(). Bijvoorbeeld:

Main.java

Main.java

copy
123456789101112131415
package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }

De methode forEach() accepteert een lambda-expressie en voert een specifieke handeling uit voor elk element in de stream.

Bijvoorbeeld:

Main.java

Main.java

copy
1234567891011
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }

Het verschil tussen deze methode en de map-methode is dat deze methode terminaal is, en daarna kun je geen andere methoden meer aanroepen.

Dit zijn alle basis methoden voor het werken met streams. Het is een complex onderwerp, en het is mogelijk dat je het niet direct begrijpt. Echter, het is een onderwerp dat je onder de knie krijgt door oefening. In de komende praktijkhoofdstukken over streams krijg je volop gelegenheid om ermee te werken, omdat het een zeer handige en praktische manier is om lijsten en arrays van data te manipuleren!

1. Wat is het primaire doel van de Stream API in Java?

2. Welke van de volgende is een terminale operatie in de Stream API?

3. Wat doet de map-bewerking in de Stream API?

4. Hoe verschilt de flatMap-bewerking van map in de Stream API?

5. Wat doet de filter-bewerking in de Stream API?

6. Wat is het doel van de forEach-bewerking in de Stream API?

7. Welke van de volgende is een intermediaire bewerking in de Stream API?

8. Hoe wordt de limit-bewerking gebruikt in de Stream API?

question mark

Wat is het primaire doel van de Stream API in Java?

Select the correct answer

question mark

Welke van de volgende is een terminale operatie in de Stream API?

Select the correct answer

question mark

Wat doet de map-bewerking in de Stream API?

Select the correct answer

question mark

Hoe verschilt de flatMap-bewerking van map in de Stream API?

Select the correct answer

question mark

Wat doet de filter-bewerking in de Stream API?

Select the correct answer

question mark

Wat is het doel van de forEach-bewerking in de Stream API?

Select the correct answer

question mark

Welke van de volgende is een intermediaire bewerking in de Stream API?

Select the correct answer

question mark

Hoe wordt de limit-bewerking gebruikt in de Stream API?

Select the correct answer

Was alles duidelijk?

Hoe kunnen we het verbeteren?

Bedankt voor je feedback!

Sectie 4. Hoofdstuk 3

Vraag AI

expand

Vraag AI

ChatGPT

Vraag wat u wilt of probeer een van de voorgestelde vragen om onze chat te starten.

Suggested prompts:

Can you explain more about how lambda expressions work with streams?

What are some common use cases for the Stream API in real-world Java projects?

Could you provide more examples of intermediate and terminal operations?

bookStream-API in Java

Veeg om het menu te tonen

Er zijn verschillende manieren om gegevens te verwerken in Java – lussen, methoden en diverse algoritmen. Echter, in Java 8 werd een zeer krachtig hulpmiddel geïntroduceerd – de Stream API.

Eenvoudig gezegd is de Stream API een manier om snel en eenvoudig te werken met een stroom van informatie. In dit geval wordt deze informatiestroom weergegeven door collecties. De Stream API kent enkele concepten. Hier zijn de belangrijkste.

Belangrijkste concepten

  • Stream: vertegenwoordigt een reeks gegevens­elementen die verwerkt kunnen worden;

  • Intermediate operations: bewerkingen die na uitvoering een nieuwe stream creëren. Voorbeelden: filter, map, distinct, sorted;

  • Terminal operations: bewerkingen die de verwerking van de stream afronden en een resultaat teruggeven. Voorbeelden: collect, forEach, count, reduce;

  • Parallel streams: maken parallelle verwerking van gegevens mogelijk. Methoden parallel() en parallelStream() worden gebruikt om parallelle streams te creëren.

Genoeg theorie, laten we beginnen met coderen!

Een stream declareren gebeurt door een methode te gebruiken op de collectie die u wilt omzetten naar een stream:

Main.java

Main.java

copy
12
List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();

Met de stream()-methode verkrijgen we een stream van strings. Om echter met de stream te werken, is het noodzakelijk te begrijpen wat lambda-expressies zijn, aangezien stream-methoden voornamelijk hiermee werken.

Lambda-expressies

Lambda-expressies zijn geïntroduceerd in Java 8 en vormen een vereenvoudigde manier om anonieme functies te creëren in Java. We hebben anonieme functies tot nu toe niet behandeld omdat ze niet strikt noodzakelijk waren, maar nu maken we er kennis mee via lambda-expressies.

Syntax van lambda-expressies:

De algemene syntax voor lambda-expressies in Java ziet er als volgt uit:

Example.java

Example.java

copy
123
(parameters) -> expression // or (parameters) -> { statements; }
  • Parameters: dit is een parameterlijst die leeg kan zijn of één of meer parameters kan bevatten;

  • Pijl: weergegeven door het symbool ->, dat de parameters scheidt van het lichaam van de lambda-expressie;

  • Expressie of statements: dit is het functielichaam, dat een expressie of een blok statements bevat.

Hier is een voorbeeld van een lambda-expressie die een eenvoudige functie voorstelt die twee getallen optelt:

Example.java

Example.java

copy
12345678910
// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;

Laten we nader bekijken wat er precies gebeurt in de bovenstaande code en hoe we lambda-expressies gebruiken:

Main.java

Main.java

copy
1234567891011121314
package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }

In de bovenstaande code:

Een functionele interface MyMathOperation aangemaakt met één abstracte methode operate.

Een lambda-expressie gebruikt om deze methode te implementeren, waarbij de optelling van twee getallen wordt uitgevoerd.

Het resultaat van de optelling afgedrukt.

Het is begrijpelijk dat het moeilijk te begrijpen kan zijn wat er in deze code gebeurt, maar laten we teruggaan naar de Stream API, waar lambda-expressies vaak worden gebruikt, en proberen te begrijpen hoe deze in de praktijk worden toegepast.

Zoals je je herinnert, hebben we eerder een stream van strings gemaakt uit een lijst van strings. Laten we nu stream-methoden gebruiken om elke string in deze stream om te zetten naar hoofdletters:

Main.java

Main.java

copy
12345678910111213
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }

In de bovenstaande code is gebruikgemaakt van een lambda-expressie en twee methoden: map() en toList(). Indien duidelijk is wat de methode toList() doet, wijzigt de methode map() elk element in de stream volgens de opgegeven lambda-expressie.

Hieronder volgt een nadere toelichting op de werking van de lambda-expressie in dit geval:

De methode map() past de methode toUpperCase() toe op elk element van de stream. Het element van deze stream is gedefinieerd als e en met behulp van de lambda-expressie wordt het programma geïnstrueerd om deze methode op elk element toe te passen.

Dit is echter nog niet het einde, omdat hier sprake is van een intermediaire bewerking. Dit betekent dat bewerkingen op de stream nog niet zijn afgerond. Om het werk op de stream af te ronden, moet een terminale bewerking worden toegepast, waarmee de bewerkingen op de stream worden beëindigd en een specifieke waarde wordt geretourneerd. Bijvoorbeeld, de methode toList() kan worden gebruikt, waarna de gewijzigde stream wordt omgezet in een lijst.

Bijvoorbeeld:

Main.java

Main.java

copy
1234567891011121314
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }

Laten we de mogelijke intermediaire bewerkingen in de stream nader bekijken.

Intermediaire bewerkingen

De map()-methode – deze methode is reeds bekend; voert bewerkingen uit die door de lambda-expressie zijn gespecificeerd op elk element in de stream.

Bijvoorbeeld, gebruik de substring()-methode op elk element in de stream van strings:

Main.java

Main.java

copy
123456789101112131415
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }

De methode filter() accepteert een lambda-expressie met een voorwaarde waarop de stream wordt gefilterd. Met andere woorden, alle elementen die aan de voorwaarde voldoen zullen in de stream blijven, en elementen die niet aan de voorwaarde voldoen worden uit de stream verwijderd. Laten we de stream aanpassen zodat alleen de elementen waarvan de lengte groter is dan 5 behouden blijven:

Main.java

Main.java

copy
12345678910111213141516
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }

Met de methode filter() wordt de string "with" uit de stream verwijderd omdat dit woord minder dan 5 tekens bevat.

Het is ook mogelijk om meerdere tussenliggende bewerkingen achter elkaar te gebruiken.

Bijvoorbeeld, de bovenstaande code kan iets worden vereenvoudigd:

Main.java

Main.java

copy
12345678910111213141516
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }

Bij het combineren van meerdere stream-methoden wordt aanbevolen om elke methode op een nieuwe regel te plaatsen om de leesbaarheid van de code aanzienlijk te verbeteren.

De methode flatMap() transformeert elk element van een stream naar een nieuwe stream en combineert de resultaten tot één enkele stream. Met andere woorden, met deze methode kunnen we de stream opdelen in meerdere streams, die vervolgens worden samengevoegd tot één stream. Stel bijvoorbeeld dat je een lijst met strings hebt, waarbij elke string meer dan één woord kan bevatten, zoals een lijst met voor- en achternamen. En je moet de eerste letter van elk van deze woorden kapitaliseren:

Main.java

Main.java

copy
123456789101112131415161718192021222324
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }

In de bovenstaande code hebben we een aparte private methode geschreven die de eerste letter van een woord kapitaliseert en deze methode gebruikt in de map()-methode samen met een lambda-expressie.

Let op dat we met de flatMap-methode elk element van de stream opdelen in verschillende streams met behulp van de methode Arrays.stream(e.split(" ")). Omdat de split()-methode een array retourneert, moeten we de methode Arrays.stream() gebruiken om deze array op te splitsen in streams.

Daarna worden al deze streams samengevoegd tot één stream, waarna we de door ons geschreven methode gebruiken. Nu hebben we alle voor- en achternamen van gebruikers met een gekapitaliseerde eerste letter.

Weet je wat handig zou zijn? Als we deze voor- en achternamen in een HashMap plaatsen, waarbij de sleutel de achternaam is en de waarde de voornaam.

Laten we dit implementeren in de code:

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334
package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }

Met een eenvoudige lus hebben we de voornaam en achternaam in variabelen opgeslagen en vervolgens in de map geplaatst. Let op hoe de lus werkt. We verhogen de variabele i met 2 bij elke iteratie omdat we de achternaam moeten overslaan zodra deze al is vastgelegd.

  • De methode distinct() verwijdert duplicaten uit de stream. In het algemeen is dit nuttig als unieke elementen in de stream vereist zijn of als u snel duplicaten uit een lijst wilt verwijderen. Dit kan eenvoudig worden bereikt met de volgende constructie:
list.stream().distinct().toList()
  • De methode sorted sorteert alle elementen in de stream in natuurlijke volgorde, van het kleinste naar het grootste getal of in alfabetische volgorde. Dit is ook nuttig als een gesorteerde stream nodig is of als u snel een lijst wilt sorteren;

  • De methode skip(n) slaat de eerste n elementen van de stream over. Dit is nuttig bij het werken met tekstbestanden, waarbij de eerste n regels bijvoorbeeld metadata of een bestandsbeschrijving kunnen zijn. Het is ook vermeldenswaardig dat de methode limit(n) doorgaans het aantal elementen in de stream beperkt. Zelfs als een stream met 1000 elementen wordt gemaakt en vervolgens limit(200) wordt gebruikt, bevat de stream slechts de eerste 200 elementen.

Main.java

Main.java

copy
123456789101112
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }

Dit zijn de belangrijkste intermediaire methoden die u moet gebruiken. U kunt de overige methoden verkennen door te verwijzen naar de link naar de officiële Java-documentatie. Laten we doorgaan naar terminalmethoden.

Terminalmethoden

  • De terminalmethode waarmee u al bekend bent, is toList(). Deze zet de stream om in een lijst en retourneert deze. Met andere woorden, u kunt deze stream met methoden direct aan een lijst toewijzen. Deze methode is geïntroduceerd in Java 17 en dient als vervanging voor de meer complexe constructie collect(Collectors.toList());

  • De collect()-methode zet de stream ook om in een specifieke datastructuur. Als parameter gebruikt deze een methode uit de Collectors-interface. Deze interface bevat methoden zoals toList(), toSet() en toCollection(). Bijvoorbeeld:

Main.java

Main.java

copy
123456789101112131415
package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }

De methode forEach() accepteert een lambda-expressie en voert een specifieke handeling uit voor elk element in de stream.

Bijvoorbeeld:

Main.java

Main.java

copy
1234567891011
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }

Het verschil tussen deze methode en de map-methode is dat deze methode terminaal is, en daarna kun je geen andere methoden meer aanroepen.

Dit zijn alle basis methoden voor het werken met streams. Het is een complex onderwerp, en het is mogelijk dat je het niet direct begrijpt. Echter, het is een onderwerp dat je onder de knie krijgt door oefening. In de komende praktijkhoofdstukken over streams krijg je volop gelegenheid om ermee te werken, omdat het een zeer handige en praktische manier is om lijsten en arrays van data te manipuleren!

1. Wat is het primaire doel van de Stream API in Java?

2. Welke van de volgende is een terminale operatie in de Stream API?

3. Wat doet de map-bewerking in de Stream API?

4. Hoe verschilt de flatMap-bewerking van map in de Stream API?

5. Wat doet de filter-bewerking in de Stream API?

6. Wat is het doel van de forEach-bewerking in de Stream API?

7. Welke van de volgende is een intermediaire bewerking in de Stream API?

8. Hoe wordt de limit-bewerking gebruikt in de Stream API?

question mark

Wat is het primaire doel van de Stream API in Java?

Select the correct answer

question mark

Welke van de volgende is een terminale operatie in de Stream API?

Select the correct answer

question mark

Wat doet de map-bewerking in de Stream API?

Select the correct answer

question mark

Hoe verschilt de flatMap-bewerking van map in de Stream API?

Select the correct answer

question mark

Wat doet de filter-bewerking in de Stream API?

Select the correct answer

question mark

Wat is het doel van de forEach-bewerking in de Stream API?

Select the correct answer

question mark

Welke van de volgende is een intermediaire bewerking in de Stream API?

Select the correct answer

question mark

Hoe wordt de limit-bewerking gebruikt in de Stream API?

Select the correct answer

Was alles duidelijk?

Hoe kunnen we het verbeteren?

Bedankt voor je feedback!

Sectie 4. Hoofdstuk 3
some-alt