Hatékony szófrekvencia-számológép Java-ban

1. Áttekintés

Ebben az oktatóanyagban bemutatjuk a szószámláló Java alkalmazásának különféle módjait.

2. Ellenmegvalósítások

Kezdjük azzal, hogy egyszerűen kiszámoljuk a tömb szavainak számát:

statikus karakterlánc [] COUNTRY_NAMES = {"Kína", "Ausztrália", "India", "USA", "Szovjetunió", "Egyesült Királyság", "Kína", "Franciaország", "Lengyelország", "Ausztria", "India" , "USA", "Egyiptom", "Kína"}; 

Ha hatalmas fájlokat akarunk feldolgozni, akkor az itt leírt egyéb lehetőségeket kell választanunk.

2.1. Térkép Val vel Egész számok

Az egyik legegyszerűbb megoldás az a Térkép, tárolja a szavakat kulcsként, és az előfordulások számát értékként:

Map counterMap = új HashMap (); for (String country: COUNTRY_NAMES) {counterMap.compute (ország, (k, v) -> v == null? 1: v + 1); } assertEquals (3, counterMap.get ("Kína"). intValue ()); assertEquals (2, counterMap.get ("India"). intValue ());

Egyszerűen használtuk TérképPraktikus kiszámít metódus, amely növeli a számlálót, vagy inicializálja 1-vel, ha a kulcs nincs meg.

Azonban, a számláló létrehozásának ez a módszere nem hatékony Egész szám megváltoztathatatlan, ezért minden alkalommal, amikor növeljük a számlálót, létrehozunk egy újat Egész szám tárgy.

2.2. Stream API

Használjuk ki a Java 8 Stream API-t párhuzamosan Patakok, és a csoportosításBy() gyűjtő:

@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 ("India"). IntValue ());} 

Hasonlóképpen használhatnánk a parallelStream:

@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 ("India"). intValue ());} 

2.3. Térkép Egy valamivel Egész szám Sor

Ezután használjuk a Térkép amely egy pultot teker egy Egész szám értékként használt tömb:

@Test public void whenMapWithPrimitiveArrayCounter_runsSuccessfully () {Map counterMap = new HashMap (); counterWithPrimitiveArray (counterMap); assertEquals (3, counterMap.get ("Kína") [0]); assertEquals (2, counterMap.get ("India") [0]); } private void counterWithPrimitiveArray (Map counterMap) {for (Karakterlánc ország: COUNTRY_NAMES) {counterMap.compute (ország, (k, v) -> v == null? új int [] {0}: v) [0] ++ ; }} 

Vegye figyelembe, hogyan hoztunk létre egy egyszerű HashMap val vel int tömbök mint értékeket.

Ban,-ben counterWithPrimitiveArray módszerrel, miközben a tömb minden egyes értékét iteráljuk, mi:

  • meghívni a kap a counterMap az országnév kulcsként történő átadásával
  • ellenőrizze, hogy van-e már kulcs vagy sem. Ha a bejegyzés már jelen van, létrehozunk egy új példányt a primitív egész tömbből, egyetlen „1” -nel. Ha a bejegyzés hiányzik, akkor növeljük a tömbben található ellenértéket

Ez a módszer jobb, mint a burkoló megvalósítása - mivel kevesebb tárgyat hoz létre.

2.4. Térkép Val,-vel MutableInteger

Ezután hozzunk létre egy burkolóobjektumot, amely beágyaz egy primitív egész számlálót az alábbiak szerint:

privát statikus osztály MutableInteger {int count = 1; public void increment () {ez.szám ++; } // getter és setter} 

Lássuk, hogyan használhatjuk számlálóként a fenti osztályt:

@Test public void whenMapWithMutableIntegerCounter_runsSuccessfully () {Map counterMap = new HashMap (); mapWithMutableInteger (counterMap); assertEquals (3, counterMap.get ("Kína"). getCount ()); assertEquals (2, counterMap.get ("India"). getCount ()); } private void counterWithMutableInteger (Map counterMap) {for (String ország: COUNTRY_NAMES) {counterMap.compute (ország, (k, v) -> v == null? új MutableInteger (0): v). növekmény (); }}

Ban,-ben mapWithMutableInteger módszerrel, miközben a COUNTRY_NAMES tömb, mi:

  • felhívni a get a counterMap az országnév kulcsként történő átadásával
  • ellenőrizze, hogy a kulcs már van-e vagy sem. Ha egy bejegyzés hiányzik, létrehozunk egy példányát MutableInteger amely a számláló értékét 1-re állítja. Növeljük a MutableInteger ha az ország jelen van a térképen

Ez a módszer a számláló létrehozására jobb, mint az előző - ahogy ugyanazt felhasználjuk MutableInteger és ezáltal kevesebb tárgyat hoz létre.

Így működik az Apache Collections HashMultiSet működik, ahol beágyazza a HashMap értékkel mint MutableInteger belsőleg.

3. Teljesítményelemzés

Itt található a diagram, amely összehasonlítja a fent felsorolt ​​módszerek teljesítményét.

A fenti diagram a JMH használatával jön létre, és itt van a fenti statisztikát létrehozó kód:

Map counterMap = új HashMap (); Map counterMutableIntMap = new HashMap (); Térkép számlálóWithIntArrayMap = új HashMap (); Térkép számlálóWithLongWrapperMap = új 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. Következtetés

Ebben a gyors cikkben bemutattuk a Java használatával a szószámlálók létrehozásának különféle módjait.

Ezeknek a példáknak a megvalósítása megtalálható a GitHub projektben - ez egy Maven-alapú projekt, ezért könnyen importálhatónak és futtathatónak kell lennie.