Java 8 og Infinite Streams
1. Oversigt
I denne artikel vil vi se på en java.util.Stream API, og vi får se, hvordan vi kan bruge den konstruktion til at fungere på en uendelig strøm af data / elementer.
Muligheden for at arbejde på den uendelige rækkefølge af elementer er baseret på det faktum, at vandløb er bygget til at være dovne.
Denne dovenskab opnås ved en adskillelse mellem to typer operationer, der kunne udføres på vandløb: mellemliggende og terminal operationer.
2. Mellemliggende og terminaloperationer
Alle Strøm operationer er opdelt i mellemliggende og terminal operationer og kombineres for at danne strømrørledninger.
En strømrørledning består af en kilde (såsom en Kollektion, en matrix, en generatorfunktion, en I / O-kanal eller en uendelig sekvensgenerator); efterfulgt af nul eller flere mellemliggende operationer og en terminaloperation.
2.1. Mellemliggende Operationer
Mellemliggende operationer udføres ikke enhed, nogle terminal operation påberåbes.
De er sammensat og danner en rørledning af en Strøm udførelse. Det mellemliggende operation kan føjes til en Strøm rørledning ved metoder:
- filter()
- kort()
- flatMap ()
- tydelig ()
- sorteret ()
- kigge ()
- begrænse()
- springe()
Alle Mellemliggende operationer er dovne, så de udføres først, før der faktisk er behov for et resultat af en behandling.
I bund og grund, mellemliggende operationer returnerer en ny stream. Udførelse af en mellemhandling udfører faktisk ikke nogen operation, men opretter i stedet en ny stream, der, når den krydses, indeholder de elementer i den oprindelige stream, der matcher det givne prædikat.
Som sådan, gennemkørsel af Strøm begynder ikke før terminal drift af rørledningen udføres.
Det er meget vigtig egenskab, specielt vigtigt for uendelige streams - fordi det giver os mulighed for at oprette streams, der faktisk kun påberåbes, når en Terminal operation kaldes.
2.2. Terminal Operationer
Terminal operationer kan krydse strømmen for at frembringe et resultat eller en bivirkning.
Når terminaloperationen er udført, betragtes strømrørledningen som forbrugt og kan ikke længere bruges. I næsten alle tilfælde er terminaloperationer ivrige og fuldfører deres gennemkørsel af datakilden og behandlingen af rørledningen inden de vender tilbage.
Ivrigheden ved en terminaloperation er vigtig med hensyn til uendelige strømme, fordi i behandlingstidspunktet er vi nødt til at tænke grundigt over, om vores Strøm er korrekt afgrænset affor eksempel en begrænse() transformation. Terminal operationer er:
- for hver()
- forEachOrdered ()
- toArray ()
- reducere()
- indsamle()
- min ()
- maks ()
- tælle()
- anyMatch ()
- allMatch ()
- noneMatch ()
- findFirst ()
- findAny ()
Hver af disse operationer udløser udførelse af alle mellemliggende operationer.
3. Uendelige strømme
Nu hvor vi forstår disse to begreber - Mellemliggende og Terminal operationer - vi er i stand til at skrive en uendelig strøm, der udnytter dovenskab af streams.
Lad os sige, at vi vil skabe en uendelig strøm af elementer fra nul, der vil blive forøget med to. Så er vi nødt til at begrænse denne sekvens, før vi kalder terminaloperation.
Det er afgørende at bruge en begrænse() metode før udførelse af en indsamle() metode det er en terminaloperation, ellers kører vores program på ubestemt tid:
// givet Stream infiniteStream = Stream.iterate (0, i -> i + 2); // når List collect = infiniteStream .limit (10) .collect (Collectors.toList ()); // derefter assertEquals (indsamle, Arrays.asList (0, 2, 4, 6, 8, 10, 12, 14, 16, 18));
Vi oprettede en uendelig strøm ved hjælp af en iterate () metode. Så kaldte vi a begrænse() transformation og en indsamle() terminal drift. Så i vores resulterende Liste, vi får de første 10 elementer i en uendelig rækkefølge på grund af en dovenskab af a Strøm.
4. Uendelig strøm af en brugerdefineret type af elementer
Lad os sige, at vi vil skabe en uendelig strøm af tilfældig UUID'er.
Det første skridt til at opnå dette ved hjælp af Strøm API er at oprette en Leverandør af disse tilfældige værdier:
Leverandør randomUUIDSupplier = UUID :: randomUUID;
Når vi definerer en leverandør, kan vi oprette en uendelig strøm ved hjælp af en frembringe() metode:
Stream infiniteStreamOfRandomUUID = Stream.generate (randomUUIDSupplier);
Så kunne vi tage et par elementer fra den strøm. Vi skal huske at bruge en begrænse() metode, hvis vi vil have vores program til at afslutte på en begrænset tid:
Liste randomInts = infiniteStreamOfRandomUUID .skip (10) .limit (10) .collect (Collectors.toList ());
Vi bruger en springe() transformation for at kassere de første 10 resultater og tage de næste 10 elementer. Vi kan oprette en uendelig strøm af enhver brugerdefineret type element ved at videregive en funktion af a Leverandør interface til en frembringe() metode på en Strøm.
6. Gøre imens - Stream Way
Lad os sige, at vi har et simpelt do.. While loop i vores kode:
int i = 0; mens (i <10) {System.out.println (i); i ++; }
Vi udskriver jeg tæller ti gange. Vi kan forvente, at en sådan konstruktion let kan skrives ved hjælp af Strøm API og ideelt set ville vi have en gøre imens() metode på en stream.
Desværre er der ingen sådan metode på en stream, og når vi ønsker at opnå funktionalitet svarende til standard gøre imens loop vi har brug for en begrænse() metode:
Stream heltal = Stream .iterate (0, i -> i + 1); heltal .limit (10) .forEach (System.out :: println);
Vi opnåede samme funktionalitet som en imperativ mens loop med mindre kode, men ring til begrænse() funktion er ikke så beskrivende, som den ville være, hvis vi havde en gøre imens() metode på en Strøm objekt.
5. Konklusion
Denne artikel forklarer, hvordan vi kan bruge Stream API at skabe uendelige strømme. Disse, når de bruges sammen med transformationer som f.eks grænse () - kan gøre nogle scenarier meget lettere at forstå og implementere.
Koden, der understøtter alle disse eksempler, findes i GitHub-projektet - dette er et Maven-projekt, så det skal være let at importere og køre som det er.