Java 8 Streams: Declaratieve benadering voor dataverwerking
Stream is een nieuwe abstracte laag geïntroduceerd in Java 8. Met streams kunt u data op een declaratieve manier verwerken, vergelijkbaar met SQL-statements. Bijvoorbeeld, de volgende SQL-statement:
SELECT max(salary), employee_id, employee_name FROM Employee
Deze SQL-expressie retourneert automatisch de details van de hoogst gesalarieerde werknemer, zonder berekeningen aan de ontwikkelaar. Met het collections-framework in Java moet een ontwikkelaar lussen gebruiken en herhaalde checks maken. Een ander punt is efficiëntie: multi-core processors zijn algemeen beschikbaar, maar parallelle code schrijven is foutgevoelig.
Om dergelijke problemen op te lossen, introduceert Java 8 het concept van stream waarmee de ontwikkelaar data declaratief kan verwerken en multi-core architecturen kan benutten zonder specifieke parallelle code te schrijven.
Wat is een Stream?
Een Stream vertegenwoordigt een reeks objecten van een bron, die aggregate operaties ondersteunt. Kenmerken:
Sequentie van elementen – Een stream levert een set elementen van een specifiek type in sequentiële volgorde. Een stream haalt/berekent elementen op aanvraag (lazy). Het slaat de elementen niet op.
Bron – Streams nemen Collections, Arrays of I/O-resources als bron.
Aggregate operaties – Streams ondersteunen filter, map, limit, reduce, find, match, etc.
Pipelining – De meeste stream-operaties retourneren de stream zelf, zodat resultaten geketend kunnen worden. Dit zijn intermediate operations.
collect()is een terminal operation die normaal aan het einde van de pipeline staat.Automatische iteraties – Stream-operaties itereren intern over de bron-elementen, in tegenstelling tot Collections waar expliciete iteratie nodig is.
Streams genereren
Met Java 8 heeft de Collection-interface twee methodes om een Stream te genereren:
stream() – Retourneert een sequentiële stream met de collection als bron.
parallelStream() – Retourneert een parallelle stream met de collection als bron.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
forEach
Stream biedt een nieuwe methode forEach om elk element van de stream te itereren. Het volgende code-segment print 10 willekeurige getallen met forEach.
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
map
De map-methode wordt gebruikt om elk element af te beelden op het bijbehorende resultaat. Het volgende segment print unieke kwadraten van getallen met map.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); List<Integer> squaresList = numbers.stream().map(i -> i*i).distinct().collect(Collectors.toList());
filter
De filter-methode elimineert elementen op basis van een criterium. Het volgende segment telt lege strings met filter.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
int count = strings.stream().filter(string -> string.isEmpty()).count();
limit
De limit-methode vermindert de grootte van de stream. Voorbeeld: 10 willekeurige getallen printen.
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
sorted
De sorted-methode sorteert de stream. Voorbeeld: 10 willekeurige getallen in gesorteerde volgorde.
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);
Parallelle verwerking
parallelStream is de alternatief voor stream voor parallelle verwerking. Voorbeeld: aantal lege strings tellen met parallelStream.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
Het is heel eenvoudig om te schakelen tussen sequentiële en parallelle streams.
Collectors
Collectors combineren het resultaat van verwerking op de elementen van een stream. Collectors kunnen een lijst of een string retourneren.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("Filtered List: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
Statistieken
Met Java 8 zijn statistics collectors geïntroduceerd om alle statistieken te berekenen tijdens stream-verwerking.
List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());
Stream Voorbeeld
Maak het volgende programma in C:\JAVA:
Java8Tester.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;
public class Java8Tester {
public static void main(String args[]) {
System.out.println("Using Java 7: ");
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("List: " + strings);
long count = getCountEmptyStringUsingJava7(strings);
System.out.println("Empty Strings: " + count);
count = getCountLength3UsingJava7(strings);
System.out.println("Strings of length 3: " + count);
List<String> filtered = deleteEmptyStringsUsingJava7(strings);
System.out.println("Filtered List: " + filtered);
String mergedString = getMergedStringUsingJava7(strings,", ");
System.out.println("Merged String: " + mergedString);
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> squaresList = getSquares(numbers);
System.out.println("Squares List: " + squaresList);
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
System.out.println("List: " + integers);
System.out.println("Highest number in List : " + getMax(integers));
System.out.println("Lowest number in List : " + getMin(integers));
System.out.println("Sum of all numbers : " + getSum(integers));
System.out.println("Average of all numbers : " + getAverage(integers));
System.out.println("Random Numbers: ");
Random random = new Random();
for(int i = 0; i < 10; i++) {
System.out.println(random.nextInt());
}
System.out.println("Using Java 8: ");
System.out.println("List: " + strings);
count = strings.stream().filter(string -> string.isEmpty()).count();
System.out.println("Empty Strings: " + count);
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("Strings of length 3: " + count);
filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("Filtered List: " + filtered);
mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
squaresList = numbers.stream().map(i -> i*i).distinct().collect(Collectors.toList());
System.out.println("Squares List: " + squaresList);
System.out.println("List: " + integers);
IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());
System.out.println("Random Numbers: ");
random.ints().limit(10).sorted().forEach(System.out::println);
count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("Empty Strings: " + count);
}
// Java 7 helper methodes (wegge laten voor beknoptheid)
private static int getCountEmptyStringUsingJava7(List<String> strings) { ... }
private static int getCountLength3UsingJava7(List<String> strings) { ... }
private static List<String> deleteEmptyStringsUsingJava7(List<String> strings) { ... }
private static String getMergedStringUsingJava7(List<String> strings, String separator) { ... }
private static List<Integer> getSquares(List<Integer> numbers) { ... }
private static int getMax(List<Integer> numbers) { ... }
private static int getMin(List<Integer> numbers) { ... }
private static int getSum(List numbers) { ... }
private static int getAverage(List<Integer> numbers) { ... }
}
Resultaat verifiëren
Compileer met javac:
C:\JAVA> javac Java8Tester.java
Voer uit:
C:\JAVA> java Java8Tester
Uitvoer (verkort):
Using Java 7: ... Using Java 8: ... Empty Strings: 2 Strings of length 3: 3 Filtered List: [abc, bc, efg, abcd, jkl] Merged String: abc, bc, efg, abcd, jkl Squares List: [9, 4, 49, 25] Highest number in List : 19 Lowest number in List : 1 Sum of all numbers : 85 Average of all numbers : 9.444444444444445 Random Numbers: (gesorteerd) Empty Strings: 2
Java
- Java-operators beheersen: typen, syntax en praktische voorbeelden
- Java-commentaar: typen, gebruik en best practices
- Java Enhanced For-lus beheersen: syntax, voorbeelden en best practices
- Java Strings beheersen: aanmaken, methodes en best practices
- Java interfaces meesteren: concepten, implementatie en best practices
- Java anonieme inner classes meesteren: definitie, syntax en praktische voorbeelden
- Java Try-with-resources meesteren: automatische resource management uitgelegd
- Java Annotaties uitgelegd: typen, plaatsing en praktische voorbeelden
- Java Assertions: een praktische gids voor bugdetectie en codebetrouwbaarheid
- Willekeurige Getallen Genereren in Java: Praktische Gids met Random en Math.random
- Java 9 Stream API-verbeteringen: takeWhile, dropWhile, iterate, ofNullable