Úvod do programu Spliterator v Jave

1. Prehľad

The Splitterator rozhranie zavedené v prostredí Java 8 môže byť používa sa na prechádzanie a rozdeľovanie sekvencií. Je to základný nástroj pre Prúdy, najmä paralelné.

V tomto článku sa budeme venovať jeho použitiu, charakteristikám, metódam a spôsobu vytvárania vlastných prispôsobených implementácií.

2. Splitterator API

2.1. tryAdvance

Toto je hlavná metóda použitá na postupnosť sekvencie. Metóda berie a Spotrebiteľ ktorý sa používa na konzumáciu prvkov Splitterator jeden po druhom postupne a vráti sa nepravdivé ak neexistujú žiadne prvky na prechádzanie.

Tu sa pozrieme na to, ako ho použiť na prechádzanie a delenie prvkov.

Najprv predpokladajme, že máme ArrayList s 35 000 článkami a tak Článok trieda je definovaná ako:

public class Article {private List listOfAuthors; private int id; súkromné ​​meno reťazca; // štandardné konštruktory / getre / setre}

Teraz implementujme úlohu, ktorá spracuje zoznam článkov a pridá príponu „- vydal Baeldung ” ku každému názvu článku:

public String call () {int current = 0; while (spliterator.tryAdvance (a -> a.setName (article.getName () .concat ("- publikoval Baeldung"))))) {current ++; } návrat Thread.currentThread (). getName () + ":" + current; }

Všimnite si, že táto úloha vydá počet článkov spracovaných po dokončení spustenia.

Ďalším kľúčovým bodom je, že sme ich použili tryAdvance () metóda na spracovanie nasledujúceho prvku.

2.2. trySplit

Ďalej sa poďme rozdeliť Štiepačky (odtiaľ názov) a oddiely spracovávať nezávisle.

The trySplit metóda sa to pokúša rozdeliť na dve časti. Potom prvky procesu volajúceho a nakoniec vrátená inštancia spracuje ostatné, čo umožní paralelné spracovanie týchto dvoch.

Najprv vygenerujme náš zoznam:

verejný statický zoznam generateElements () {návrat Stream.generate (() -> nový článok ("Java")) .limit (35000) .collect (Collectors.toList ()); }

Ďalej získavame naše Splitterator napríklad pomocou rozdeľovač () metóda. Potom aplikujeme naše trySplit () metóda:

@Test public void givenSpliterator_whenAppliedToAListOfArticle_thenSplittedInHalf () {Spliterator split1 = Executor.generateElements (). Spliterator (); Spliterator split2 = split1.trySplit (); assertThat (new Task (split1) .call ()) .containsSequence (Executor.generateElements (). size () / 2 + ""); assertThat (new Task (split2) .call ()) .containsSequence (Executor.generateElements (). size () / 2 + ""); }

Proces rozdelenia fungoval podľa plánu a záznamy boli rozdelené rovnako.

2.3. odhadovaná veľkosť

The odhadovaná veľkosť metóda nám poskytuje odhadovaný počet prvkov:

LOG.info ("Veľkosť:" + split1.estimateSize ());

Zobrazí sa:

Veľkosť: 17500

2.4. máCharakteristiky

Toto API kontroluje, či sa dané charakteristiky zhodujú s vlastnosťami súboru Splitterator. Potom, ak vyvoláme vyššie uvedenú metódu, výstup bude int týchto charakteristík:

LOG.info ("Charakteristika:" + split1.characteristics ());
Charakteristika: 16464

3. Splitterator Charakteristiky

Má osem rôznych charakteristík, ktoré popisujú jeho správanie. Môžu byť použité ako pomôcka pre externé nástroje:

  • VEĽKÁ ak je schopný vrátiť presný počet prvkov pomocou odhadVeľkosť () metóda
  • Triedené - ak iteruje prostredníctvom triedeného zdroja
  • SUBSIZOVANÉ - ak rozdelíme inštanciu pomocou a trySplit () metódou a získať splitterátory, ktoré sú VEĽKÁ tiež
  • SÚČASNÝ - ak je možné zdroj bezpečne meniť súčasne
  • ODLIŠNÝ - ak pre každú dvojicu narazených prvkov x, y,! x.equals (y)
  • NEZMENNÉ - ak prvky držané zdrojom nemožno štrukturálne upraviť
  • NIE JE NEPLATNÉ - ak zdroj obsahuje nulové hodnoty alebo nie
  • OBJEDNANÉ - ak iteruje nad usporiadanou sekvenciou

4. Vlastné Splitterator

4.1. Kedy prispôsobiť

Najprv predpokladajme nasledujúci scenár:

Máme triedu článkov so zoznamom autorov a článok, ktorý môže mať viac ako jedného autora. Ďalej považujeme autora súvisiaceho s článkom, ak sa ID jeho súvisiaceho článku zhoduje s ID článku.

Náš Autor trieda bude vyzerať takto:

public class Autor {private String name; private int relatedArticleId; // štandardní hľadači, zakladatelia a konštruktéri}

Ďalej implementujeme triedu na počítanie autorov a prechádzame prúdom autorov. Potom trieda vykoná redukciu na potoku.

Pozrime sa na implementáciu triedy:

public class RelatedAuthorCounter {private int counter; private boolean isVzťahuje sa; // štandardné konštruktory / getre verejné RelatedAuthorCounter sa hromadia (autor autor) {if (author.getRelatedArticleId () == 0) {return isRelated? toto: new RelatedAuthorCounter (counter, true); } else {návrat jeVzťahuje sa? nový RelatedAuthorCounter (counter + 1, false): this; }} public CombinAuthorCounter combine (RelatedAuthorCounter RelatedAuthorCounter) {vrátiť nový RelatedAuthorCounter (counter + RelatedAuthorCounter.counter, RelatedAuthorCounter.isRelated); }}

Každá metóda vo vyššie uvedenej triede vykonáva konkrétnu operáciu, ktorá sa má počítať pri prechode.

Po prvé, akumulovať () metóda prechádza autorov po jednom iteračným spôsobompotom kombinovať () sčíta dva počítadlá pomocou ich hodnôt. Nakoniec getCounter () vráti počítadlo.

Teraz otestujeme, čo sme doteraz urobili. Poďme previesť zoznam autorov nášho článku na prúd autorov:

Stream stream = article.getListOfAuthors (). Stream ();

A implementovať a countAuthor () spôsob vykonania redukcie na streame pomocou RelatedAuthorCounter:

private int countAutors (Stream stream) {RelatedAuthorCounter wordCounter = stream.reduce (new RelatedAuthorCounter (0, true), RelatedAuthorCounter :: akumulovať, RelatedAuthorCounter :: kombinovať); návrat wordCounter.getCounter (); }

Ak sme použili postupný prúd, výstup bude podľa očakávania „Count = 9“, problém však nastáva, keď sa pokúsime operáciu paralelizovať.

Pozrime sa na nasledujúci testovací prípad:

@Test void givenAStreamOfAuthors_whenProcessedInParallel_countProducesWrongOutput () {assertThat (Executor.countAutors (stream.parallel ())). IsGreaterThan (9); }

Zrejme sa niečo pokazilo - rozdelenie streamu na náhodnú pozíciu spôsobilo, že autor bol započítaný dvakrát.

4.2. Ako prispôsobiť

Aby sme to vyriešili, musíme realizovať a Splitterator ktorá autorov rozdeľuje, iba ak sú v príbuzenskom vzťahu id a articleId zápasy. Tu je implementácia nášho zvyku Splitterator:

verejná trieda RelatedAuthorSpliterator implementuje Spliterator {súkromný konečný zoznam; AtomicInteger current = nový AtomicInteger (); // štandardný konštruktor / getre @Override public boolean tryAdvance (akcia spotrebiteľa) {action.accept (list.get (current.getAndIncrement ())); vrátiť current.get () <list.size (); } @Override public Spliterator trySplit () {int currentSize = list.size () - current.get (); if (currentSize <10) {return null; } for (int splitPos = currentSize / 2 + current.intValue (); splitPos <list.size (); splitPos ++) {if (list.get (splitPos) .getRelatedArticleId () == 0) {Spliterator spliterator = new RelatedAuthorSpliterator ( list.subList (current.get (), splitPos)); current.set (splitPos); spätný rozdeľovač; }} return null; } @Override public long estimSize () {return list.size () - current.get (); } @Override public int characteristics () {return CONCURRENT; }}

Teraz sa prihlasujem countAuthors () metóda poskytne správny výstup. Nasledujúci kód demonštruje, že:

@Test public void givenAStreamOfAuthors_whenProcessedInParallel_countProducesRightOutput () {Stream stream2 = StreamSupport.stream (spliter, true); assertThat (Executor.countAutors (stream2.parallel ())). isEqualTo (9); }

Tiež zvyk Splitterator je vytvorený zo zoznamu autorov a prechádza ním podržaním aktuálnej polohy.

Pozrime sa podrobnejšie na implementáciu jednotlivých metód:

  • tryAdvance odovzdáva autorov Spotrebiteľ na aktuálnej pozícii indexu a zvyšuje jeho pozíciu
  • trySplit definuje štiepací mechanizmus, v našom prípade RelatedAuthorSpliterator sa vytvorí, keď sa ID zhodujú, a rozdelením sa zoznam rozdelí na dve časti
  • odhadovaná veľkosť - je rozdiel medzi veľkosťou zoznamu a pozíciou aktuálne iterovaného autora
  • charakteristiky- vráti Splitterator charakteristiky, v našom prípade VEĽKÁ ako hodnota vrátená odhadovaná veľkosť () metóda je presná; navyše SÚČASNÝ naznačuje, že zdroj tohto Splitterator môžu byť bezpečne upravené inými vláknami

5. Podpora pre primitívne hodnoty

The SplitteratorAPI podporuje primitívne hodnoty vrátane dvojitý, int a dlho.

Jediný rozdiel medzi použitím generického a primitívneho dedikovaného Splitterator je dané Spotrebiteľ a typ Splitterator.

Napríklad, keď to potrebujeme na int hodnotu, ktorú musíme zložiť intConsumer. Ďalej je tu zoznam primitívnych venovaných Štiepačky:

  • OfPrimitive: nadradené rozhranie pre ďalšie primitívy
  • OfInt: A Splitterator špecializované pre int
  • OfDouble: A Splitterator určené pre dvojitý
  • OfLong: A Splitterator určené pre dlho

6. Záver

V tomto článku sme sa venovali Java 8 Splitterator použitie, metódy, vlastnosti, proces rozdelenia, primitívna podpora a spôsob jej prispôsobenia.

Celú implementáciu tohto článku nájdete ako vždy na serveri Github.


$config[zx-auto] not found$config[zx-overlay] not found