Effektiv Word Frequency Calculator i Java

1. Oversikt

I denne veiledningen viser vi forskjellige måter å implementere en ordteller på Java.

2. Motimplementeringer

La oss starte med å bare beregne ordtellingen av ord i denne matrisen:

statisk streng [] COUNTRY_NAMES = {"Kina", "Australia", "India", "USA", "Sovjetunionen", "Storbritannia", "Kina", "Frankrike", "Polen", "Østerrike", "India" , "USA", "Egypt", "Kina"}; 

Hvis vi ønsker å behandle store filer, må vi gå etter andre alternativer beskrevet her.

2.1. Kart Med Heltall

En av de enkleste løsningene ville være å lage en Kart, lagre ord som nøkler og antall forekomster som verdier:

Kart counterMap = ny HashMap (); for (Stringland: COUNTRY_NAMES) {counterMap.compute (country, (k, v) -> v == null? 1: v + 1); } assertEquals (3, counterMap.get ("Kina"). intValue ()); assertEquals (2, counterMap.get ("India"). intValue ());

Vi brukte rett og slett Kart‘S handy beregne metode som øker disken eller initialiserer den med 1 hvis nøkkelen ikke er til stede.

Derimot, denne metoden for å lage teller er ikke effektiv som Heltall er uforanderlig, så hver gang vi øker disken, lager vi en ny Heltall gjenstand.

2.2. Stream API

La oss nå utnytte Java 8 Stream API, parallelt Strømmer, og gruppering av() samler:

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

På samme måte kan vi bruke en parallelStream:

@Test offentlig ugyldig når MapWithLambdaAndWrapperCounter_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. Kart Med en Heltall Array

La oss deretter bruke en Kart som bryter en teller i en Heltall array brukt som verdi:

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

Legg merke til hvordan vi opprettet en enkel HashMap med int arrays som verdier.

I counterWithPrimitiveArray metode, mens vi gjentar over hver verdi i matrisen, vi:

  • påkalle a counterMap ved å sende landnavnet som en nøkkel
  • sjekk om en nøkkel allerede var tilstede eller ikke. Hvis oppføringen allerede er til stede, oppretter vi en ny forekomst av primitivt heltall-array med en enkelt "1". Hvis oppføringen er fraværende, øker vi motverdien som er tilstede i matrisen

Denne metoden er bedre enn implementeringen av innpakningen - ettersom det skaper færre gjenstander.

2.4. Kart Med en MutableInteger

Deretter la oss lage et innpakningsobjekt som legger inn en primitiv heltalsteller som nedenfor:

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

La oss se hvordan vi kan bruke ovennevnte klasse som en teller:

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

I mapWithMutableInteger metoden, mens den gjentas over hvert land i LAND_NAVN array, vi:

  • påkalle en komme på counterMap ved å gi landnavnet som en nøkkel
  • sjekk om nøkkelen allerede er tilstede eller ikke. Hvis en oppføring er fraværende, oppretter vi en forekomst av MutableInteger som setter motverdien som 1. Vi øker motverdien som er tilstede i MutableInteger hvis landet er til stede på kartet

Denne metoden for å lage en teller er bedre enn den forrige - når vi bruker det samme MutableInteger og derved skape færre gjenstander.

Dette er hvordan Apache Collections HashMultiSet fungerer der det bygger inn en HashMap med verdi som MutableInteger internt.

3. Prestasjonsanalyse

Her er diagrammet som sammenligner ytelsen til hver metode som er oppført ovenfor.

Ovenstående diagram er opprettet ved hjelp av JMH, og her er koden som skapte statistikken ovenfor:

Kart counterMap = nytt HashMap (); Map counterMutableIntMap = ny HashMap (); Map counterWithIntArrayMap = ny HashMap (); KarttellerWithLongWrapperMap = nytt HashMap (); @Benchmark public void wrapperAsCounter () {counterWithWrapperObject (counterMap); } @Benchmark public void lambdaExpressionWithWrapper () {counterWithLambdaAndWrapper (counterWithLongWrapperMap); } @Benchmark offentlig ugyldig parallelStreamWithWrapper () {counterWithParallelStreamAndWrapper (counterWithLongWrapperStreamMap); } @Benchmark public void mutableIntegerAsCounter () {counterWithMutableInteger (counterMutableIntMap); } @Benchmark public void mapWithPrimitiveArray () {counterWithPrimitiveArray (counterWithIntArrayMap); } 

4. Konklusjon

I denne raske artikkelen illustrerte vi forskjellige måter å lage ordtellere ved hjelp av Java.

Implementeringen av disse eksemplene finnes i GitHub-prosjektet - dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.


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