1. Lijst met methoden van de Streamklasse

De Streamklasse 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 Streamobjecten 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 Streamklasse (alleen de meest elementaire):

methoden Beschrijving
Stream<T> of()
Creëert een stream van een set objecten
Stream<T> generate()
Genereert een stream volgens de opgegeven regel
Stream<T> concat()
Voegt twee streams samen
Stream<T> filter()
Filtert de gegevens en geeft alleen gegevens door die overeenkomen met de opgegeven regel
Stream<T> distinct()
Verwijdert duplicaten. Geeft geen gegevens door die al zijn aangetroffen
Stream<T> sorted()
Sorteert de gegevens
Stream<T> peek()
Voert een actie uit op elk element in de stream
Stream<T> limit(n)
Retourneert een stroom die is afgekapt zodat deze niet langer is dan de opgegeven limiet
Stream<T> skip(n)
Slaat de eerste n elementen over
Stream<R> map()
Converteert gegevens van het ene type naar het andere
Stream<R> flatMap()
Converteert gegevens van het ene type naar het andere
boolean anyMatch()
Controleert of er ten minste één element in de stream is dat overeenkomt met de opgegeven regel
boolean allMatch()
Controleert of alle elementen in de stream overeenkomen met de opgegeven regel
boolean noneMatch()
Controleert of geen van de elementen in de stream overeenkomt met de opgegeven regel
Optional<T> findFirst()
Retourneert het eerste gevonden element dat overeenkomt met de regel
Optional<T> findAny()
Retourneert elk element in de stroom dat overeenkomt met de regel
Optional<T> min()
Zoekt naar het minimale element in de gegevensstroom
Optional<T> max()
Retourneert het maximale element in de gegevensstroom
long count()
Retourneert het aantal elementen in de gegevensstroom
R collect()
Leest alle gegevens uit de stream en retourneert deze als een verzameling

2. Tussen- en eindoperaties door de Streamklas

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 Streamklasse 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.

Methodeaanroeppijplijn

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();
Algemeen uiterlijk van een pijpleiding

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 Selectorklas, 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 mainmethode. 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:

.flatMap(owner -> owner.getPets().stream())
Ga van a Stream<Owner>naar aStream<Pet>
.filter(pet -> Cat.class.equals(pet.getClass()))
Bewaar alleen katten in de gegevensstroom
.filter(cat -> Color.FOXY == cat.getColor())
Bewaar alleen rode katten in de gegevensstroom
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
Sorteer op leeftijd in aflopende volgorde
.map(Animal::getName)
Krijg de namen
.collect(Collectors.toList())
Zet het resultaat in een lijst

3. Streams maken

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

Bewaar alleen nummers kleiner dan drie
Stream<Integer> stream = Stream.of(1, -2, 3, -4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x > 0));

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::printlnaan 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 nelementen 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). nVoorbeeld:

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