1. Lijst met methoden van de Stream
klasse
De Stream
klasse is gemaakt om het gemakkelijk te maken om ketens van gegevensstromen te construeren . Om dit te bereiken, Stream<T>
heeft de klasse methoden die nieuwe Stream
objecten retourneren.
Elk van deze datastromen voert één simpele actie uit, maar als je ze combineert tot ketens en interessante lambda-functies toevoegt , dan heb je een krachtig mechanisme om de gewenste output te genereren. Binnenkort zul je het zelf zien.
Dit zijn de methoden van de Stream
klasse (alleen de meest elementaire):
methoden | Beschrijving |
---|---|
|
Creëert een stream van een set objecten |
|
Genereert een stream volgens de opgegeven regel |
|
Voegt twee streams samen |
|
Filtert de gegevens en geeft alleen gegevens door die overeenkomen met de opgegeven regel |
|
Verwijdert duplicaten. Geeft geen gegevens door die al zijn aangetroffen |
|
Sorteert de gegevens |
|
Voert een actie uit op elk element in de stream |
|
Retourneert een stroom die is afgekapt zodat deze niet langer is dan de opgegeven limiet |
|
Slaat de eerste n elementen over |
|
Converteert gegevens van het ene type naar het andere |
|
Converteert gegevens van het ene type naar het andere |
|
Controleert of er ten minste één element in de stream is dat overeenkomt met de opgegeven regel |
|
Controleert of alle elementen in de stream overeenkomen met de opgegeven regel |
|
Controleert of geen van de elementen in de stream overeenkomt met de opgegeven regel |
|
Retourneert het eerste gevonden element dat overeenkomt met de regel |
|
Retourneert elk element in de stroom dat overeenkomt met de regel |
|
Zoekt naar het minimale element in de gegevensstroom |
|
Retourneert het maximale element in de gegevensstroom |
|
Retourneert het aantal elementen in de gegevensstroom |
|
Leest alle gegevens uit de stream en retourneert deze als een verzameling |
2. Tussen- en eindoperaties door de Stream
klas
Zoals u kunt zien, retourneren niet alle methoden in de bovenstaande tabel een Stream
. Dit hangt samen met het feit dat de methoden van de Stream
klasse kunnen worden onderverdeeld in tussenliggende (ook wel niet-terminale ) methoden en terminale methoden.
Tussenliggende methoden
Tussenliggende methoden retourneren een object dat de interface implementeert Stream
, en ze kunnen aan elkaar worden geketend.
Terminale methoden
Terminalmethoden retourneren een andere waarde dan een Stream
.
Methodeaanroeppijplijn
U kunt dus een stroompijplijn bouwen die bestaat uit een willekeurig aantal tussenliggende methoden en aan het einde een enkele terminalmethodeaanroep. Met deze aanpak kunt u vrij complexe logica implementeren, terwijl de leesbaarheid van de code wordt vergroot.

De gegevens in een gegevensstroom veranderen helemaal niet. Een keten van tussenliggende methoden is een gelikte (declaratieve) manier om een pijplijn voor gegevensverwerking te specificeren die zal worden uitgevoerd nadat de terminalmethode is aangeroepen.
Met andere woorden, als de terminalmethode niet wordt aangeroepen, worden de gegevens in de gegevensstroom op geen enkele manier verwerkt. Pas nadat de terminalmethode is aangeroepen, beginnen de gegevens te worden verwerkt volgens de regels die zijn gespecificeerd in de stroompijplijn.
stream()
.intemediateOperation1()
.intemediateOperation2()
...
.intemediateOperationN()
.terminalOperation();
Vergelijking van tussenliggende en terminale methoden:
tussenliggend | terminal | |
---|---|---|
Retoursoort | Stream |
niet eenStream |
Kan worden gecombineerd met meerdere methoden van hetzelfde type om een pijplijn te vormen | Ja | Nee |
Aantal methoden in één pijplijn | elk | niet meer dan één |
Produceert het eindresultaat | Nee | Ja |
Begint met het verwerken van de gegevens in de stream | Nee | Ja |
Laten we naar een voorbeeld kijken.
Stel, we hebben een club voor dierenliefhebbers. Morgen viert de club Ginger Cat Day. De club heeft eigenaren van gezelschapsdieren, die elk een lijst met huisdieren hebben. Ze zijn niet beperkt tot katten.
Taak: je moet alle namen van alle rode katten identificeren om gepersonaliseerde wenskaarten voor hen te maken voor de "professionele vakantie" van morgen. De wenskaarten moeten worden gesorteerd op leeftijd van de kat, van de oudste naar de jongste.
Eerst bieden we enkele klassen om deze taak te helpen oplossen:
public enum Color {
WHITE,
BLACK,
DARK_GREY,
LIGHT_GREY,
FOXY,
GREEN,
YELLOW,
BLUE,
MAGENTA
}
public abstract class Animal {
private String name;
private Color color;
private int age;
public Animal(String name, Color color, int age) {
this.name = name;
this.color = color;
this.age = age;
}
public String getName() {
return name;
}
public Color getColor() {
return color;
}
public int getAge() {
return age;
}
}
public class Cat extends Animal {
public Cat(String name, Color color, int age) {
super(name, color, age);
}
}
public class Dog extends Animal {
public Dog(String name, Color color, int age) {
super(name, color, age);
}
}
public class Parrot extends Animal {
public Parrot(String name, Color color, int age) {
super(name, color, age);
}
}
public class Pig extends Animal {
public Pig(String name, Color color, int age) {
super(name, color, age);
}
}
public class Snake extends Animal {
public Snake(String name, Color color, int age) {
super(name, color, age);
}
}
public class Owner {
private String name;
private List<Animal> pets = new ArrayList<>();
public Owner(String name) {
this.name = name;
}
public List<Animal> getPets() {
return pets;
}
}
Laten we nu eens kijken naar de Selector
klas, waar de selectie zal worden gemaakt volgens de gespecificeerde criteria:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Selector {
private static List<Owner> owners;
private static void initData() {
final Owner owner1 = new Owner("Ronan Turner");
owner1.getPets().addAll(List.of(
new Cat("Baron", Color.BLACK, 3),
new Cat("Sultan", Color.DARK_GREY, 4),
new Dog("Elsa", Color.WHITE, 0)
));
final Owner owner2 = new Owner("Scarlet Murray");
owner2.getPets().addAll(List.of(
new Cat("Ginger", Color.FOXY, 7),
new Cat("Oscar", Color.FOXY, 5),
new Parrot("Admiral", Color.BLUE, 3)
));
final Owner owner3 = new Owner("Felicity Mason");
owner3.getPets().addAll(List.of(
new Dog("Arnold", Color.FOXY, 3),
new Pig("Vacuum Cleaner", Color.LIGHT_GREY, 8)
));
final Owner owner4 = new Owner("Mitchell Stone");
owner4.getPets().addAll(List.of(
new Snake("Mr. Boa", Color.DARK_GREY, 2)
));
final Owner owner5 = new Owner("Jonathan Snyder");
owner5.getPets().addAll(List.of(
new Cat("Fisher", Color.BLACK, 16),
new Cat("Zorro", Color.FOXY, 14),
new Cat("Margo", Color.WHITE, 3),
new Cat("Brawler", Color.DARK_GREY, 1)
));
owners = List.of(owner1, owner2, owner3, owner4, owner5);
}
}
Wat overblijft is het toevoegen van code aan de main
methode. Momenteel roepen we eerst de initData()
methode aan, die de lijst met eigenaren van gezelschapsdieren in de club vult. Vervolgens selecteren we de namen van roodharige katten, gesorteerd op leeftijd in aflopende volgorde.
Laten we eerst kijken naar code die geen streams gebruikt om deze taak op te lossen:
public static void main(String[] args) {
initData();
List<String> findNames = new ArrayList<>();
List<Cat> findCats = new ArrayList<>();
for (Owner owner : owners) {
for (Animal pet : owner.getPets()) {
if (Cat.class.equals(pet.getClass()) && Color.FOXY == pet.getColor()) {
findCats.add((Cat) pet);
}
}
}
Collections.sort(findCats, new Comparator<Cat>() {
public int compare(Cat o1, Cat o2) {
return o2.getAge() - o1.getAge();
}
});
for (Cat cat : findCats) {
findNames.add(cat.getName());
}
findNames.forEach(System.out::println);
}
Laten we nu eens kijken naar een alternatief:
public static void main(String[] args) {
initData();
final List<String> findNames = owners.stream()
.flatMap(owner -> owner.getPets().stream())
.filter(pet -> Cat.class.equals(pet.getClass()))
.filter(cat -> Color.FOXY == cat.getColor())
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.map(Animal::getName)
.collect(Collectors.toList());
findNames.forEach(System.out::println);
}
Zoals je kunt zien, is de code veel compacter. Bovendien is elke regel van de stroompijplijn een enkele actie, zodat ze kunnen worden gelezen als zinnen in het Engels:
|
Ga van a Stream<Owner> naar aStream<Pet> |
|
Bewaar alleen katten in de gegevensstroom |
|
Bewaar alleen rode katten in de gegevensstroom |
|
Sorteer op leeftijd in aflopende volgorde |
|
Krijg de namen |
|
Zet het resultaat in een lijst |
3. Streams maken
De Stream
klasse heeft drie methoden die we nog niet hebben behandeld. Het doel van deze drie methoden is om nieuwe threads te maken.
Stream<T>.of(T obj)
methode
De of()
methode creëert een stream die uit een enkel element bestaat. Dit is meestal nodig als bijvoorbeeld een functie een Stream<T>
object als argument neemt, maar u alleen een T
object hebt. Dan kun je eenvoudig en eenvoudig de of()
methode gebruiken om een stream te krijgen die uit een enkel element bestaat .
Voorbeeld:
Stream<Integer> stream = Stream.of(1);
Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)
methode
De of()
methode creëert een stroom die bestaat uit doorgegeven elementen . Elk aantal elementen is toegestaan. Voorbeeld:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<T> Stream.generate(Supplier<T> obj)
methode
Met deze generate()
methode kun je een regel instellen die wordt gebruikt om het volgende element van de stream te genereren wanneer daarom wordt gevraagd. U kunt bijvoorbeeld elke keer een willekeurig getal uitdelen .
Voorbeeld:
Stream<Double> s = Stream.generate(Math::random);
Stream<T> Stream.concat(Stream<T> a, Stream<T> b)
methode
De concat()
methode voegt de twee doorgegeven streams samen tot één . Wanneer de gegevens worden gelezen, wordt deze eerst uit de eerste stroom gelezen en vervolgens uit de tweede. Voorbeeld:
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = Stream.of(10, 11, 12, 13, 14);
Stream<Integer> result = Stream.concat(stream1, stream2);
4. Gegevens filteren
Nog eens 6 methoden creëren nieuwe gegevensstromen, waardoor je stromen kunt combineren tot ketens (of pijplijnen) van verschillende complexiteit.
Stream<T> filter(Predicate<T>)
methode
Deze methode retourneert een nieuwe gegevensstroom die de brongegevensstroom filtert volgens de doorgegeven regel . De methode moet worden aangeroepen op een object waarvan het type Stream<T>
.
U kunt de filterregel specificeren met behulp van een lambda-functie , die de compiler vervolgens zal converteren naar een Predicate<T>
object.
Voorbeelden:
Geketende stromen | Uitleg |
---|---|
|
Bewaar alleen nummers kleiner dan drie |
|
Bewaar alleen getallen groter dan nul |
Stream<T> sorted(Comparator<T>)
methode
Deze methode retourneert een nieuwe gegevensstroom die de gegevens in de bronstroom sorteert . U geeft een comparator door , die de regels vaststelt voor het vergelijken van twee elementen van de gegevensstroom.
Stream<T> distinct()
methode
Deze methode retourneert een nieuwe gegevensstroom die alleen de unieke elementen in de brongegevensstroom bevat . Alle dubbele gegevens worden weggegooid. Voorbeeld:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.distinct(); // 1, 2, 3, 4, 5
Stream<T> peek(Consumer<T>)
methode
Deze methode retourneert een nieuwe gegevensstroom , hoewel de gegevens daarin dezelfde zijn als in de bronstroom. Maar wanneer het volgende element uit de stream wordt opgevraagd, wordt de functie die u aan de peek()
methode hebt doorgegeven, daarmee aangeroepen.
Als u de functie doorgeeft System.out::println
aan de peek()
methode, worden alle objecten weergegeven wanneer ze door de stream gaan.
Stream<T> limit(int n)
methode
Deze methode retourneert een nieuwe gegevensstroom die alleen de eerste n
elementen in de brongegevensstroom bevat . Alle andere gegevens worden weggegooid. Voorbeeld:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.limit(3); // 1, 2, 3
Stream<T> skip(int n)
methode
Deze methode retourneert een nieuwe gegevensstroom die dezelfde elementen bevat als de bronstroom , maar de eerste elementen overslaat (negeert). n
Voorbeeld:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.skip(3); // 4, 5, 2, 2, 2, 3, 4
GO TO FULL VERSION