1. Liste over klassens Streammetoder

Klassen Streamblev oprettet for at gøre det nemt at konstruere kæder af datastrømme. For at opnå dette Stream<T>har klassen metoder, der returnerer nye Streamobjekter.

Hver af disse datastrømme udfører en simpel handling, men hvis du kombinerer dem i kæder og tilføjer interessante lambda-funktioner , så har du en kraftfuld mekanisme til at generere det output, du ønsker. Snart vil du se for dig selv.

Her er klassens metoder Stream(kun de mest basale):

Metoder Beskrivelse
Stream<T> of()
Opretter en strøm fra et sæt objekter
Stream<T> generate()
Genererer en strøm i henhold til den angivne regel
Stream<T> concat()
Sammenkæder to strømme
Stream<T> filter()
Filtrerer dataene og videregiver kun data, der matcher den angivne regel
Stream<T> distinct()
Fjerner dubletter. Videregiver ikke data, der allerede er stødt på
Stream<T> sorted()
Sorterer dataene
Stream<T> peek()
Udfører en handling på hvert element i strømmen
Stream<T> limit(n)
Returnerer en strøm, der er afkortet, så den ikke er længere end den angivne grænse
Stream<T> skip(n)
Springer de første n elementer over
Stream<R> map()
Konverterer data fra en type til en anden
Stream<R> flatMap()
Konverterer data fra en type til en anden
boolean anyMatch()
Kontrollerer, om der er mindst ét ​​element i strømmen, der matcher den angivne regel
boolean allMatch()
Kontrollerer, om alle elementerne i strømmen matcher den angivne regel
boolean noneMatch()
Kontrollerer, om ingen af ​​elementerne i strømmen matcher den angivne regel
Optional<T> findFirst()
Returnerer det første fundne element, der matcher reglen
Optional<T> findAny()
Returnerer ethvert element i strømmen, der matcher reglen
Optional<T> min()
Søger efter minimumselementet i datastrømmen
Optional<T> max()
Returnerer det maksimale element i datastrømmen
long count()
Returnerer antallet af elementer i datastrømmen
R collect()
Læser alle data fra streamen og returnerer dem som en samling

2. Mellemliggende og terminale operationer af Streamklassen

Som du kan se, returnerer ikke alle metoder i tabellen ovenfor en Stream. Dette hænger sammen med, at klassens metoder Streamkan opdeles i mellemliggende (også kendt som ikke-terminale ) metoder og terminale metoder.

Mellemmetoder

Mellemliggende metoder returnerer et objekt, der implementerer Streamgrænsefladen, og de kan kædes sammen.

Terminal metoder

Terminalmetoder returnerer en anden værdi end en Stream.

Metodekald pipeline

Således kan du bygge en strømpipeline bestående af et vilkårligt antal mellemliggende metoder og et enkelt terminalmetodekald til sidst. Denne tilgang giver dig mulighed for at implementere temmelig kompleks logik, mens du øger kodelæsbarheden.

Metodekald pipeline

Dataene i en datastrøm ændres overhovedet ikke. En kæde af mellemliggende metoder er en smart (deklarativ) måde at specificere en databehandlingspipeline på, som vil blive eksekveret, efter at terminalmetoden er kaldt.

Med andre ord, hvis terminalmetoden ikke kaldes, så behandles dataene i datastrømmen ikke på nogen måde. Først efter at terminalmetoden er kaldt, begynder dataene at blive behandlet i overensstemmelse med de regler, der er specificeret i strømpipelinen.

stream()
  .intemediateOperation1()
  .intemediateOperation2()
  ...
  .intemediateOperationN()
  .terminalOperation();
Generelt udseende af en rørledning

Sammenligning af mellem- og terminalmetoder:

mellemliggende terminal
Returtype Stream ikke aStream
Kan kombineres med flere metoder af samme type for at danne en rørledning Ja ingen
Antal metoder i en enkelt pipeline nogen ikke mere end én
Producerer slutresultatet ingen Ja
Begynder at behandle dataene i strømmen ingen Ja

Lad os se på et eksempel.

Antag, at vi har en klub for dyreelskere. I morgen fejrer klubben Ginger Cat Day. Klubben har kæledyrsejere, som hver har en liste over kæledyr. De er ikke begrænset til katte.

Opgave: du skal identificere alle navnene på alle ingefærkattene for at skabe personlige lykønskningskort til dem til morgendagens "professionelle ferie". Lykønskningskortene skal sorteres efter kattens alder, fra ældste til yngste.

Først giver vi nogle klasser for at hjælpe med at løse denne opgave:

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

Lad os nu se på Selectorklassen, hvor valget vil blive foretaget i henhold til de angivne kriterier:

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

Tilbage er at tilføje kode til mainmetoden. I øjeblikket kalder vi først initData()metoden, som udfylder listen over kæledyrsejere i klubben. Derefter vælger vi navnene på ingefærkatte sorteret efter deres alder i faldende rækkefølge.

Lad os først se kode, der ikke bruger streams til at løse denne opgave:

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

Lad os nu 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 meget mere kompakt. Derudover er hver linje i strømpipelinen en enkelt handling, så de kan læses som sætninger på engelsk:

.flatMap(owner -> owner.getPets().stream())
Gå fra a Stream<Owner>til aStream<Pet>
.filter(pet -> Cat.class.equals(pet.getClass()))
Behold kun katte i datastrømmen
.filter(cat -> Color.FOXY == cat.getColor())
Behold kun ingefærkatte i datastrømmen
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
Sorter efter alder i faldende rækkefølge
.map(Animal::getName)
Få navnene
.collect(Collectors.toList())
Sæt resultatet på en liste

3. Oprettelse af streams

Klassen Streamhar tre metoder, som vi ikke har dækket endnu. Formålet med disse tre metoder er at skabe nye tråde.

Stream<T>.of(T obj)metode

Metoden of()skaber en strøm , der består af et enkelt element. Dette er normalt nødvendigt, når f.eks. en funktion tager et Stream<T>objekt som et argument, men du kun har et Tobjekt. Så kan du nemt og enkelt bruge of()metoden til at få en strøm , der består af et enkelt element .

Eksempel:

Stream<Integer> stream = Stream.of(1);

Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)metode

Metoden of()opretter en strøm , der består af beståede elementer . Et hvilket som helst antal elementer er tilladt. Eksempel:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

Stream<T> Stream.generate(Supplier<T> obj)metode

Metoden generate()giver dig mulighed for at indstille en regel, der vil blive brugt til at generere det næste element i strømmen, når det anmodes om det. For eksempel kan du give et tilfældigt tal hver gang.

Eksempel:

Stream<Double> s = Stream.generate(Math::random);

Stream<T> Stream.concat(Stream<T> a, Stream<T> b)metode

Metoden concat()sammenkæder de to passerede strømme til én . Når dataene læses, læses de først fra den første strøm og derefter fra den anden. 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 af data

Yderligere 6 metoder skaber nye datastrømme, så du kan kombinere strømme til kæder (eller pipelines) af varierende kompleksitet.

Stream<T> filter(Predicate<T>)metode

Denne metode returnerer en ny datastrøm , der filtrerer kildedatastrømmen i henhold til den beståede regel . Metoden skal kaldes på et objekt , hvis type er Stream<T>.

Du kan angive filtreringsreglen ved hjælp af en lambda-funktion , som compileren derefter konverterer til et Predicate<T>objekt.

Eksempler:

Lænkede vandløb Forklaring
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x < 3));

Behold kun tal mindre end tre
Stream<Integer> stream = Stream.of(1, -2, 3, -4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x > 0));

Bevar kun tal større end nul

Stream<T> sorted(Comparator<T>)metode

Denne metode returnerer en ny datastrøm, der sorterer dataene i kildestrømmen . Du sender en komparator ind , som sætter reglerne for sammenligning af to elementer i datastrømmen.

Stream<T> distinct()metode

Denne metode returnerer en ny datastrøm , der kun indeholder de unikke elementer i kildedatastrømmen . Alle duplikerede data kasseres. 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 metode returnerer en ny datastrøm , selvom dataene i den er de samme som i kildestrømmen. Men når det næste element bliver anmodet om fra strømmen, kaldes den funktion , du sendte til peek()metoden, med den.

Hvis du videregiver funktionen System.out::printlntil peek()metoden, vil alle objekterne blive vist, når de passerer gennem åen.

Stream<T> limit(int n)metode

Denne metode returnerer en ny datastrøm , der kunn indeholder de første elementer i kildedatastrømmen . Alle andre data kasseres. 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 metode returnerer en ny datastrøm , der indeholder alle de samme elementer som kildestrømmen , men springer over (ignorerer) de første nelementer. 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