1. Liste over klassens Streammetoder

Klassen Streamble opprettet for å gjøre det enkelt å konstruere kjeder av datastrømmer. For å oppnå dette Stream<T>har klassen metoder som returnerer nye Streamobjekter.

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 Streamklassen (bare de mest grunnleggende):

Metoder Beskrivelse
Stream<T> of()
Oppretter en strøm fra et sett med objekter
Stream<T> generate()
Genererer en strøm i henhold til den angitte regelen
Stream<T> concat()
Sammenkobler to bekker
Stream<T> filter()
Filtrerer dataene og sender bare data som samsvarer med den angitte regelen
Stream<T> distinct()
Fjerner duplikater. Sender ikke data som allerede er påtruffet
Stream<T> sorted()
Sorterer dataene
Stream<T> peek()
Utfører en handling på hvert element i strømmen
Stream<T> limit(n)
Returnerer en strøm som er avkortet slik at den ikke er lengre enn den angitte grensen
Stream<T> skip(n)
Hopper over de første n elementene
Stream<R> map()
Konverterer data fra en type til en annen
Stream<R> flatMap()
Konverterer data fra en type til en annen
boolean anyMatch()
Sjekker om det er minst ett element i strømmen som samsvarer med den angitte regelen
boolean allMatch()
Sjekker om alle elementene i strømmen samsvarer med den angitte regelen
boolean noneMatch()
Sjekker om ingen av elementene i strømmen samsvarer med den angitte regelen
Optional<T> findFirst()
Returnerer det første elementet funnet som samsvarer med regelen
Optional<T> findAny()
Returnerer ethvert element i strømmen som samsvarer med regelen
Optional<T> min()
Søker etter minimumselementet i datastrømmen
Optional<T> max()
Returnerer maksimumselementet i datastrømmen
long count()
Returnerer antall elementer i datastrømmen
R collect()
Leser alle dataene fra strømmen og returnerer dem som en samling

2. Mellom- og terminaloperasjoner av Streamklassen

Som du kan se, returnerer ikke alle metodene i tabellen ovenfor en Stream. Dette er relatert til det faktum at metodene til Streamklassen 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.

Metodeanropsrørledning

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();
Generelt utseende til en rørledning

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å Selectorklassen, 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 mainmetoden. 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:

.flatMap(owner -> owner.getPets().stream())
Gå fra a Stream<Owner>til aStream<Pet>
.filter(pet -> Cat.class.equals(pet.getClass()))
Behold kun katter i datastrømmen
.filter(cat -> Color.FOXY == cat.getColor())
Behold kun ingefærkatter i datastrømmen
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
Sorter etter alder i synkende rekkefølge
.map(Animal::getName)
Få navnene
.collect(Collectors.toList())
Sett resultatet inn i en liste

3. Opprette strømmer

Klassen Streamhar 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 Tobjekt. 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
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x < 3));

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

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::printlntil 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 nelementene. 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