Efektívna kalkulačka frekvencie slov v Jave

1. Prehľad

V tejto príručke si ukážeme rôzne spôsoby implementácie počítadla slov v prostredí Java.

2. Protiopatrenia

Začnime jednoduchým výpočtom počtu slov v tomto poli:

static String [] COUNTRY_NAMES = {"Čína", "Austrália", "India", "USA", "ZSSR", "Spojené kráľovstvo", "Čína", "Francúzsko", "Poľsko", "Rakúsko", "India" , „USA“, „Egypt“, „Čína“}; 

Ak chceme spracovať veľké súbory, musíme ísť po ďalších možnostiach, ktoré sú tu popísané.

2.1. Mapa S Celé čísla

Jedným z najjednoduchších riešení by bolo vytvoriť a Mapa, ukladať slová ako kľúče a počet výskytov ako hodnoty:

Počítadlo mapy = nová HashMap (); pre (Krajina reťazca: COUNTRY_NAMES) {counterMap.compute (krajina, (k, v) -> v == null? 1: v + 1); } assertEquals (3, counterMap.get ("Čína"). intValue ()); assertEquals (2, counterMap.get ("India"). intValue ());

Jednoducho sme použili MapaJe šikovný vypočítať metóda, ktorá zvyšuje počítadlo alebo ho inicializuje na 1, ak kľúč nie je prítomný.

Avšak tento spôsob vytvárania počítadla nie je efektívny ako Celé číslo je nemenná, takže zakaždým, keď zvýšime počítadlo, vytvoríme nový Celé číslo objekt.

2.2. Stream API

Teraz využime paralelne rozhranie Java 8 Stream API Prúdya zoskupenieBy() zberateľ:

@Test public void whenMapWithLambdaAndWrapperCounter_runsSuccessfully () {Map counterMap = new HashMap (); Stream.of (COUNTRY_NAMES) .collect (Collectors.groupingBy (k -> k, () -> counterMap, Collectors.counting ()); assertEquals (3, counterMap.get ("Čína"). IntValue ()); assertEquals (2, counterMap.get ("India"). IntValue ());} 

Podobne by sme mohli použiť a paralelný prúd:

@Test public void whenMapWithLambdaAndWrapperCounter_runsSuccessfully () {Map counterMap = new HashMap (); Stream.of (COUNTRY_NAMES) .parallel () .collect (Collectors.groupingBy (k -> k, () -> counterMap, Collectors.counting ()); assertEquals (3, counterMap.get ("Čína"). IntValue ( )); assertEquals (2, counterMap.get ("India"). intValue ());} 

2.3. Mapa S Celé číslo Pole

Ďalej použijeme a Mapa ktorý zabalí počítadlo do Celé číslo pole použité ako hodnota:

@Test public void whenMapWithPrimitiveArrayCounter_runsSuccessfully () {Map counterMap = new HashMap (); counterWithPrimitiveArray (counterMap); assertEquals (3, counterMap.get ("Čína") [0]); assertEquals (2, counterMap.get ("India") [0]); } private void counterWithPrimitiveArray (Map counterMap) {for (String country: COUNTRY_NAMES) {counterMap.compute (country, (k, v) -> v == null? new int [] {0}: v) [0] ++ ; }} 

Všimnite si, ako sme vytvorili jednoduchý HashMap s int polia ako hodnoty.

V counterWithPrimitiveArray metóda, pri iterácii nad každou hodnotou poľa, sme:

  • vyvolať a dostať na counterMap odovzdaním názvu krajiny ako kľúča
  • skontrolovať, či kľúč už bol alebo nebol. Ak je záznam už k dispozícii, vytvoríme novú inštanciu primitívneho celého čísla s jedinou „1“. Ak záznam chýba, zvýšime hodnotu počítadla v poli

Táto metóda je lepšia ako implementácia wrapperu - pretože vytvára menej objektov.

2.4. Mapa S MutableInteger

Ďalej vytvoríme obalový objekt, ktorý obsahuje primitívne celočíselné počítadlo, ako je uvedené nižšie:

súkromná statická trieda MutableInteger {int count = 1; public void increment () {this.count ++; } // zakladač a nastavovač} 

Pozrime sa, ako môžeme využiť vyššie uvedenú triedu ako počítadlo:

@Test public void whenMapWithMutableIntegerCounter_runsSuccessfully () {Map counterMap = new HashMap (); mapWithMutableInteger (counterMap); assertEquals (3, counterMap.get ("Čína"). getCount ()); assertEquals (2, counterMap.get ("India"). getCount ()); } private void counterWithMutableInteger (Map counterMap) {for (String country: COUNTRY_NAMES) {counterMap.compute (country, (k, v) -> v == null? new MutableInteger (0): v) .increment (); }}

V mapWithMutableInteger metódou, zatiaľ čo iterácia nad každou krajinou v COUNTRY_NAMES pole, my:

  • vyvolať dostať na counterMap odovzdaním názvu krajiny ako kľúča
  • skontrolujte, či je kľúč už prítomný alebo nie. Ak záznam chýba, vytvoríme inštanciu MutableInteger ktorá nastavuje hodnotu počítadla na 1. Zvyšujeme hodnotu počítadla prítomnú v MutableInteger ak je krajina na mape

Táto metóda vytvorenia počítadla je lepšia ako predchádzajúca - pretože opakovane používame to isté MutableInteger a tým vytvára menej objektov.

Takto fungujú zbierky Apache HashMultiSet funguje tam, kde je to vložené a HashMap s hodnotou ako MutableInteger vnútorne.

3. Analýza výkonu

Tu je tabuľka, ktorá porovnáva výkonnosť každej z vyššie uvedených metód.

Graf vyššie sa vytvára pomocou nástroja JMH a tu je kód, ktorý vytvoril štatistiku vyššie:

Počítadlo mapy = nová HashMap (); Map counterMutableIntMap = new HashMap (); Map counterWithIntArrayMap = new HashMap (); Počítadlo mapyWithLongWrapperMap = nový HashMap (); @Benchmark public void wrapperAsCounter () {counterWithWrapperObject (counterMap); } @Benchmark public void lambdaExpressionWithWrapper () {counterWithLambdaAndWrapper (counterWithLongWrapperMap); } @Benchmark public void parallelStreamWithWrapper () {counterWithParallelStreamAndWrapper (counterWithLongWrapperStreamMap); } @Benchmark public void mutableIntegerAsCounter () {counterWithMutableInteger (counterMutableIntMap); } @Benchmark public void mapWithPrimitiveArray () {counterWithPrimitiveArray (counterWithIntArrayMap); } 

4. Záver

V tomto rýchlom článku sme si ilustrovali rôzne spôsoby vytvárania počítadiel slov pomocou Javy.

Implementáciu týchto príkladov možno nájsť v projekte GitHub - jedná sa o projekt založený na Maven, takže by malo byť ľahké ho importovať a spustiť tak, ako je.


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