Učinkovit kalkulator frekvencije riječi u Javi

1. Pregled

U ovom uputstvu pokazat ćemo razne načine implementacije brojača riječi u Javi.

2. Protupostavke

Počnimo s jednostavnim izračunavanjem broja riječi u ovom nizu:

statički niz [] COUNTRY_NAMES = {"Kina", "Australija", "Indija", "SAD", "SSSR", "UK", "Kina", "Francuska", "Poljska", "Austrija", "Indija" , "SAD", "Egipat", "Kina"}; 

Ako želimo obraditi ogromne datoteke, moramo potražiti druge ovdje opisane opcije.

2.1. Karta S Cijeli brojevi

Jedno od najjednostavnijih rješenja bilo bi stvaranje a Karta, pohrani riječi kao ključeve, a broj pojavljivanja kao vrijednosti:

Map counterMap = novi HashMap (); za (Zemlja niza: COUNTRY_NAMES) {counterMap.compute (zemlja, (k, v) -> v == null? 1: v + 1); } assertEquals (3, counterMap.get ("Kina"). intValue ()); assertEquals (2, counterMap.get ("Indija"). intValue ());

Jednostavno smo koristili Karta‘Zgodan izračunati metoda koja povećava brojač ili ga inicijalizira s 1 ako ključ nije prisutan.

Međutim, ovaj način stvaranja brojača nije učinkovit kao Cijeli broj je nepromjenjiv, pa svaki put kada povećavamo brojač, stvaramo novi Cijeli broj objekt.

2.2. Stream API

Ajmo sada, paralelno iskoristiti Java 8 Stream API Potoci, i grupiranjeBy() sakupljač:

@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 ("China"). IntValue ()); assertEquals (2, counterMap.get ("Indija"). IntValue ());} 

Slično tome, mogli bismo koristiti a paralelni tok:

@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 ("China"). IntValue ( )); assertEquals (2, counterMap.get ("Indija"). intValue ());} 

2.3. Karta S an Cijeli broj Polje

Dalje, upotrijebimo a Karta koji obavija brojač unutar Cijeli broj niz koji se koristi kao vrijednost:

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

Imajte na umu kako smo stvorili jednostavan HashMap s int nizova kao vrijednosti.

U counterWithPrimitiveArray metodom, dok ponavljamo svaku vrijednost niza, mi:

  • prizivati ​​a dobiti na counterMap prosljeđivanjem naziva države kao ključa
  • provjerite je li ključ već bio prisutan ili nije. Ako je unos već prisutan, kreiramo novu instancu primitivnog čitavog niza s jednim "1". Ako unosa nema, povećavamo vrijednost brojača prisutnu u polju

Ova metoda je bolja od implementacije omota - jer stvara manje predmeta.

2.4. Karta S PromjenjiviInteger

Dalje, kreirajmo objekt omotača koji ugrađuje primitivni brojač cijelih brojeva kao što je prikazano dolje:

privatna statička klasa MutableInteger {int count = 1; prirast javne praznine () {this.count ++; } // dobivač i postavljač} 

Pogledajmo kako gore navedenu klasu možemo koristiti kao brojač:

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

U mapWithMutableInteger metodom, dok se ponavlja kroz svaku zemlju u COUNTRY_NAMES niz, mi:

  • pozvati dobiti na counterMap prosljeđivanjem naziva države kao ključa
  • provjerite je li ključ već prisutan ili nije. Ako je unos odsutan, kreiramo primjerak PromjenjiviInteger koja postavlja vrijednost brojača na 1. Povećavamo vrijednost brojača prisutnu u Promjenjivi cijeli broj ako je zemlja prisutna na karti

Ova metoda stvaranja brojača bolja je od prethodne - kao što ponovno koristimo isti PromjenjiviInteger a time i stvaranje manje predmeta.

Ovako rade Apache kolekcije HashMultiSet radi tamo gdje ugrađuje a HashMap s vrijednošću kao PromjenjiviInteger iznutra.

3. Analiza izvedbe

Evo tablice koja uspoređuje izvedbu svake gore navedene metode.

Iznad grafikon je stvoren pomoću JMH, a ovdje je kod koji je stvorio gornju statistiku:

Map counterMap = novi HashMap (); Map counterMutableIntMap = novi HashMap (); Map counterWithIntArrayMap = novi HashMap (); Map counterWithLongWrapperMap = novi HashMap (); @Benchmark javna void wrapperAsCounter () {counterWithWrapperObject (counterMap); } @Benchmark javna void lambdaExpressionWithWrapper () {counterWithLambdaAndWrapper (counterWithLongWrapperMap); } @Benchmark javna praznina paralelno StreamWithWrapper () {counterWithParallelStreamAndWrapper (counterWithLongWrapperStreamMap); } @Benchmark javna void mutableIntegerAsCounter () {counterWithMutableInteger (counterMutableIntMap); } @Benchmark javna void kartaWithPrimitiveArray () {counterWithPrimitiveArray (counterWithIntArrayMap); } 

4. Zaključak

U ovom kratkom članku ilustrirali smo razne načine stvaranja brojača riječi pomoću Jave.

Implementacija ovih primjera može se naći u projektu GitHub - ovo je projekt zasnovan na Mavenu, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.


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