1. Liste over klassens Stream
metoder
Klassen Stream
ble opprettet for å gjøre det enkelt å konstruere kjeder av datastrømmer. For å oppnå dette Stream<T>
har klassen metoder som returnerer nye Stream
objekter.
Hver av disse datastrømmene utfører én enkel handling, men hvis du kombinerer dem til kjeder og legger til interessante lambda-funksjoner , har du en kraftig mekanisme for å generere utdataene du ønsker. Snart vil du se selv.
Her er metodene for Stream
klassen (bare de mest grunnleggende):
Metoder | Beskrivelse |
---|---|
|
Oppretter en strøm fra et sett med objekter |
|
Genererer en strøm i henhold til den angitte regelen |
|
Sammenkobler to bekker |
|
Filtrerer dataene og sender bare data som samsvarer med den angitte regelen |
|
Fjerner duplikater. Sender ikke data som allerede er påtruffet |
|
Sorterer dataene |
|
Utfører en handling på hvert element i strømmen |
|
Returnerer en strøm som er avkortet slik at den ikke er lengre enn den angitte grensen |
|
Hopper over de første n elementene |
|
Konverterer data fra en type til en annen |
|
Konverterer data fra en type til en annen |
|
Sjekker om det er minst ett element i strømmen som samsvarer med den angitte regelen |
|
Sjekker om alle elementene i strømmen samsvarer med den angitte regelen |
|
Sjekker om ingen av elementene i strømmen samsvarer med den angitte regelen |
|
Returnerer det første elementet funnet som samsvarer med regelen |
|
Returnerer ethvert element i strømmen som samsvarer med regelen |
|
Søker etter minimumselementet i datastrømmen |
|
Returnerer maksimumselementet i datastrømmen |
|
Returnerer antall elementer i datastrømmen |
|
Leser alle dataene fra strømmen og returnerer dem som en samling |
2. Mellom- og terminaloperasjoner av Stream
klassen
Som du kan se, returnerer ikke alle metodene i tabellen ovenfor en Stream
. Dette er relatert til det faktum at metodene til Stream
klassen kan deles inn i mellomliggende (også kjent som ikke-terminale ) metoder og terminalmetoder .
Mellommetoder
Mellommetoder returnerer et objekt som implementerer grensesnittet Stream
, og de kan lenkes sammen.
Terminalmetoder
Terminalmetoder returnerer en annen verdi enn en Stream
.
Metodeanropsrørledning
Dermed kan du bygge en strømrørledning som består av et hvilket som helst antall mellommetoder og et enkelt terminalmetodekall på slutten. Denne tilnærmingen lar deg implementere ganske kompleks logikk, samtidig som kodelesbarheten økes.
Dataene i en datastrøm endres ikke i det hele tatt. En kjede av mellommetoder er en smidig (deklarativ) måte å spesifisere en databehandlingspipeline som vil bli utført etter at terminalmetoden er kalt.
Med andre ord, hvis terminalmetoden ikke kalles, blir ikke dataene i datastrømmen behandlet på noen måte. Først etter at terminalmetoden er kalt, begynner dataene å bli behandlet i henhold til reglene spesifisert i strømrørledningen.
stream()
.intemediateOperation1()
.intemediateOperation2()
...
.intemediateOperationN()
.terminalOperation();
Sammenligning av mellom- og terminalmetoder:
mellomliggende | terminal | |
---|---|---|
Returtype | Stream |
ikke aStream |
Kan kombineres med flere metoder av samme type for å danne en rørledning | ja | Nei |
Antall metoder i en enkelt rørledning | noen | ikke mer enn én |
Gir sluttresultatet | Nei | ja |
Begynner å behandle dataene i strømmen | Nei | ja |
La oss se på et eksempel.
Tenk deg at vi har en klubb for dyreelskere. I morgen feirer klubben Ginger Cat Day. Klubben har kjæledyreiere, som hver har en liste over kjæledyr. De er ikke begrenset til katter.
Oppgave: du må identifisere alle navnene på alle ingefærkattene for å lage personlige gratulasjonskort for dem for morgendagens "profesjonelle ferie". Gratulasjonskortene skal sorteres etter kattens alder, fra eldste til yngste.
Først gir vi noen klasser for å hjelpe deg med å løse denne oppgaven:
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;
}
}
La oss nå se på Selector
klassen, der utvalget vil bli gjort i henhold til de angitte kriteriene:
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);
}
}
Det som gjenstår er å legge til kode i main
metoden. Foreløpig kaller vi først initData()
metoden, som fyller listen over dyreeiere i klubben. Deretter velger vi navnene på ingefærkatter sortert etter alder i synkende rekkefølge.
La oss først se på kode som ikke bruker strømmer for å løse denne oppgaven:
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);
}
La oss nå se på et alternativ:
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);
}
Som du kan se, er koden mye mer kompakt. I tillegg er hver linje i strømledningen en enkelt handling, slik at de kan leses som setninger på engelsk:
|
Gå fra a Stream<Owner> til aStream<Pet> |
|
Behold kun katter i datastrømmen |
|
Behold kun ingefærkatter i datastrømmen |
|
Sorter etter alder i synkende rekkefølge |
|
Få navnene |
|
Sett resultatet inn i en liste |
3. Opprette strømmer
Klassen Stream
har tre metoder som vi ikke har dekket ennå. Hensikten med disse tre metodene er å lage nye tråder.
Stream<T>.of(T obj)
metode
Metoden of()
lager en strøm som består av et enkelt element. Dette er vanligvis nødvendig når for eksempel en funksjon tar et Stream<T>
objekt som et argument, men du bare har et T
objekt. Da kan du enkelt og greit bruke of()
metoden for å få en strøm som består av ett enkelt element .
Eksempel:
Stream<Integer> stream = Stream.of(1);
Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)
metode
Metoden of()
lager en strøm som består av beståtte elementer . Et hvilket som helst antall elementer er tillatt. Eksempel:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<T> Stream.generate(Supplier<T> obj)
metode
Metoden generate()
lar deg angi en regel som skal brukes til å generere det neste elementet i strømmen når det blir bedt om det. Du kan for eksempel gi ut et tilfeldig tall hver gang.
Eksempel:
Stream<Double> s = Stream.generate(Math::random);
Stream<T> Stream.concat(Stream<T> a, Stream<T> b)
metode
Metoden concat()
setter de to passerte strømmene sammen til én . Når dataene leses, leses de først fra den første strømmen, og deretter fra den andre. Eksempel:
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. Filtrering av data
Ytterligere 6 metoder skaper nye datastrømmer, som lar deg kombinere strømmer til kjeder (eller rørledninger) med varierende kompleksitet.
Stream<T> filter(Predicate<T>)
metode
Denne metoden returnerer en ny datastrøm som filtrerer kildedatastrømmen i henhold til den godkjente regelen . Metoden må kalles på et objekt med typen Stream<T>
.
Du kan spesifisere filtreringsregelen ved å bruke en lambda-funksjon , som kompilatoren deretter konverterer til et Predicate<T>
objekt.
Eksempler:
Lenkede bekker | Forklaring |
---|---|
|
Behold bare tall mindre enn tre |
|
Behold bare tall større enn null |
Stream<T> sorted(Comparator<T>)
metode
Denne metoden returnerer en ny datastrøm som sorterer dataene i kildestrømmen . Du sender inn en komparator , som setter reglene for sammenligning av to elementer i datastrømmen.
Stream<T> distinct()
metode
Denne metoden returnerer en ny datastrøm som bare inneholder de unike elementene i kildedatastrømmen . Alle dupliserte data blir forkastet. Eksempel:
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>)
metode
Denne metoden returnerer en ny datastrøm , selv om dataene i den er de samme som i kildestrømmen. Men når det neste elementet blir forespurt fra strømmen, kalles funksjonen du sendte til metoden med den.peek()
Hvis du sender funksjonen System.out::println
til peek()
metoden, vil alle objektene vises når de vil passere gjennom bekken.
Stream<T> limit(int n)
metode
Denne metoden returnerer en ny datastrøm som baren
inneholder de første elementene i kildedatastrømmen . Alle andre data blir forkastet. Eksempel:
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)
metode
Denne metoden returnerer en ny datastrøm som inneholder alle de samme elementene som kildestrømmen , men hopper over (ignorerer) de første n
elementene. Eksempel:
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