Effektiv ordfrekvensberegner i Java

1. Oversigt

I denne vejledning viser vi forskellige måder at implementere en ordtæller på Java.

2. Modimplementeringer

Lad os starte med blot at beregne ordtællingen af ​​ord i denne matrix:

statisk streng [] COUNTRY_NAMES = {"Kina", "Australien", "Indien", "USA", "USSR", "UK", "Kina", "Frankrig", "Polen", "Østrig", "Indien" , "USA", "Egypten", "Kina"}; 

Hvis vi vil behandle store filer, skal vi gå til andre muligheder, der er beskrevet her.

2.1. Kort Med Heltal

En af de enkleste løsninger ville være at skabe en Kort, gem ord som nøgler og antallet af forekomster som værdier:

Kort counterMap = nyt HashMap (); for (String country: COUNTRY_NAMES) {counterMap.compute (country, (k, v) -> v == null? 1: v + 1); } assertEquals (3, counterMap.get ("Kina"). intValue ()); assertEquals (2, counterMap.get ("Indien"). intValue ());

Vi brugte simpelthen KortDet er praktisk beregne metode, der øger tælleren eller initialiserer den med 1, hvis nøglen ikke er til stede.

Imidlertid, denne metode til oprettelse af tæller er ikke effektiv som Heltal er uforanderlig, så hver gang vi øger tælleren, skaber vi et nyt Heltal objekt.

2.2. Stream API

Lad os nu udnytte Java 8 Stream API parallelt Strømme, og grupperingBy() samler:

@Test offentligt ugyldigt nårMapWithLambdaAndWrapperCounter_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 ("Indien"). IntValue ());} 

På samme måde kunne vi bruge en parallelStream:

@Test offentligt ugyldigt nårMapWithLambdaAndWrapperCounter_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 ("Indien"). intValue ());} 

2.3. Kort Med en Heltal Array

Lad os derefter bruge en Kort der indpakker en tæller i en Heltal array anvendt som en værdi:

@Test offentlig ugyldig nårMapWithPrimitiveArrayCounter_runsSuccessfully () {Map counterMap = new HashMap (); counterWithPrimitiveArray (counterMap); assertEquals (3, counterMap.get ("Kina") [0]); assertEquals (2, counterMap.get ("Indien") [0]); } privat ugyldig counterWithPrimitiveArray (Map counterMap) {for (String country: COUNTRY_NAMES) {counterMap.compute (country, (k, v) -> v == null? new int [] {0}: v) [0] ++ ; }} 

Bemærk hvordan vi oprettede en simpel HashMap med int-arrays som værdier.

I counterWithPrimitiveArray metode, mens vi gentager hver værdi i arrayet, vi:

  • påberåbe sig en på den counterMap ved at sende landets navn som en nøgle
  • kontrollere, om en nøgle allerede var til stede eller ej. Hvis posten allerede er til stede, opretter vi en ny forekomst af primitivt heltal array med en enkelt "1". Hvis posten er fraværende, øger vi den modværdi, der er til stede i arrayet

Denne metode er bedre end implementeringen af ​​indpakningen - da det skaber færre objekter.

2.4. Kort Med en MutableInteger

Lad os derefter oprette et indpakningsobjekt, der integrerer en primitiv heltalstæller som nedenfor:

privat statisk klasse MutableInteger {int count = 1; offentlig tomrumsforøgelse () {this.count ++; } // getter og setter} 

Lad os se, hvordan vi kan bruge ovennævnte klasse som en tæller:

@Test offentlig ugyldig nårMapWithMutableIntegerCounter_runsSuccessfully () {Map counterMap = new HashMap (); mapWithMutableInteger (counterMap); assertEquals (3, counterMap.get ("Kina"). getCount ()); assertEquals (2, counterMap.get ("Indien"). getCount ()); } privat ugyldigt counterWithMutableInteger (Map counterMap) {for (String country: COUNTRY_NAMES) {counterMap.compute (country, (k, v) -> v == null? new MutableInteger (0): v) .increment (); }}

I mapWithMutableInteger metode, mens det gentager sig over hvert land i LAND_NAVN array, vi:

  • påberåbe sig en komme på counterMap ved at sende landets navn som en nøgle
  • Kontroller, om nøglen allerede er til stede eller ej. Hvis en post er fraværende, opretter vi en forekomst af MutableInteger der indstiller tællerværdien som 1. Vi øger tællerværdien til stede i MutableInteger hvis landet er til stede på kortet

Denne metode til at oprette en tæller er bedre end den forrige - som vi genbruger det samme MutableInteger og derved skabe færre objekter.

Sådan Apache Collections HashMultiSet fungerer hvor det indlejrer en HashMap med værdi som MutableInteger internt.

3. Præstationsanalyse

Her er diagrammet, der sammenligner ydeevnen for hver enkelt metode, der er anført ovenfor.

Ovenstående diagram oprettes ved hjælp af JMH, og her er koden, der oprettede statistikken ovenfor:

Kort counterMap = nyt HashMap (); Map counterMutableIntMap = ny HashMap (); Map counterWithIntArrayMap = ny HashMap (); Map counterWithLongWrapperMap = ny HashMap (); @Benchmark public void wrapperAsCounter () {counterWithWrapperObject (counterMap); } @Benchmark offentlig ugyldighed lambdaExpressionWithWrapper () {counterWithLambdaAndWrapper (counterWithLongWrapperMap); } @Benchmark offentlig ugyldig parallelStreamWithWrapper () {counterWithParallelStreamAndWrapper (counterWithLongWrapperStreamMap); } @Benchmark public void mutableIntegerAsCounter () {counterWithMutableInteger (counterMutableIntMap); } @Benchmark offentligt ugyldigt mapWithPrimitiveArray () {counterWithPrimitiveArray (counterWithIntArrayMap); } 

4. Konklusion

I denne hurtige artikel illustrerede vi forskellige måder at oprette ordtællere på ved hjælp af Java.

Implementeringen af ​​disse eksempler findes i GitHub-projektet - dette er et Maven-baseret projekt, så det skal være let at importere og køre som det er.


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