Guava-cache

1. Oversigt

I denne tutorial tager vi et kig på Guava-cache implementering - grundlæggende brug, bortkastningspolitikker, opdatering af cachen og nogle interessante bulkoperationer.

Endelig vil vi se på brugen af ​​de fjernelsesmeddelelser, som cachen kan sende.

2. Sådan bruges Guava Cache

Lad os starte med et simpelt eksempel - lad os cache den store bogstav af Snor tilfælde.

Først opretter vi CacheLoader - bruges til at beregne den værdi, der er gemt i cachen. Fra dette bruger vi det praktiske CacheBuilder at opbygge vores cache ved hjælp af de givne specifikationer:

@Test offentlig ugyldigt nårCacheMiss_thenValueIsComputed () {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder (). build (loader); assertEquals (0, cache.size ()); assertEquals ("HELLO", cache.getUnchecked ("hej")); assertEquals (1, cache.size ()); }

Læg mærke til, hvordan der ikke er nogen værdi i cachen for vores "hej" -tast - og værdien beregnes og caches.

Bemærk også, at vi bruger getUchchecked () operation - dette beregner og indlæser værdien i cachen, hvis den ikke allerede findes.

3. Udkastelsespolitikker

Hver cache skal fjerne værdier på et eller andet tidspunkt. Lad os diskutere mekanismen til at fjerne værdier ud af cachen - ved hjælp af forskellige kriterier.

3.1. Udkast efter størrelse

Vi kan begrænse størrelsen på vores cache ved brug af maximumSize (). Hvis cachen når grænsen, fjernes de ældste emner.

I den følgende kode begrænser vi cachestørrelsen til 3 poster:

@Test offentligt ugyldigt nårCacheReachMaxSize_thenEviction () {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder (). maximumSize (3) .build (loader); cache.getUnchecked ("første"); cache.getUnchecked ("anden"); cache.getUnchecked ("tredje"); cache.getUnchecked ("frem"); assertEquals (3, cache.size ()); assertNull (cache.getIfPresent ("første")); assertEquals ("FORTH", cache.getIfPresent ("frem")); }

3.2. Udkast efter vægt

Det kan vi også begrænse cache-størrelsen ved hjælp af en brugerdefineret vægtfunktion. I den følgende kode bruger vi længde som vores brugerdefinerede vægtfunktion:

@Test offentligt ugyldigt nårCacheReachMaxWeight_thenEviction () {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; Weigher weighByLength; weighByLength = new Weigher () {@Override public int weigh (String key, String value) {return value.length (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder () .maximumWeight (16). weigher (weighByLength) .build (loader); cache.getUnchecked ("første"); cache.getUnchecked ("anden"); cache.getUnchecked ("tredje"); cache.getUnchecked ("sidste"); assertEquals (3, cache.size ()); assertNull (cache.getIfPresent ("første")); assertEquals ("LAST", cache.getIfPresent ("sidste")); }

Bemærk: Cachen fjerner muligvis mere end en post for at give plads til en ny stor.

3.3. Udkast efter tid

Udover at bruge størrelse til at udskyde gamle poster kan vi bruge tid. I det følgende eksempel tilpasser vi vores cache til fjern poster, der har været inaktive i 2 ms:

@Test offentlig ugyldig nårEntryIdle_thenEviction () kaster InterruptedException {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder () .expireAfterAccess (2, TimeUnit.MILLISECONDS) .build (loader); cache.getUnchecked ("hej"); assertEquals (1, cache.size ()); cache.getUnchecked ("hej"); Tråd. Søvn (300); cache.getUnchecked ("test"); assertEquals (1, cache.size ()); assertNull (cache.getIfPresent ("hej")); }

Det kan vi også udsætte poster baseret på deres samlede live-tid. I det følgende eksempel fjerner cachen posterne efter lagring af 2 ms:

@Test offentligt ugyldigt nårEntryLiveTimeExpire_thenEviction () kaster InterruptedException {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder () .expireAfterWrite (2, TimeUnit.MILLISECONDS) .build (loader); cache.getUnchecked ("hej"); assertEquals (1, cache.size ()); Tråd. Søvn (300); cache.getUnchecked ("test"); assertEquals (1, cache.size ()); assertNull (cache.getIfPresent ("hej")); }

4. Svage taster

Lad os derefter se, hvordan vi får vores cache-nøgler til at have svage referencer - så affaldssamleren kan indsamle cache-nøgler, der ikke henvises til andetsteds.

Som standard har både cache-nøgler og værdier stærke referencer, men vi kan få vores cache til at gemme nøglerne ved hjælp af svage referencer ved hjælp af svage nøgler () som i følgende eksempel:

@Test offentlig ugyldig nårWeakKeyHasNoRef_thenRemoveFromCache () {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder (). weakKeys (). build (loader); }

5. Bløde værdier

Vi kan tillade affaldssamleren at indsamle vores cachelagrede værdier ved hjælp af softValues ​​() som i følgende eksempel:

@Test offentlig ugyldig nårSoftValue_thenRemoveFromCache () {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder (). softValues ​​(). build (loader); }

Bemærk: Mange bløde referencer kan påvirke systemets ydeevne - det foretrækkes at bruge maximumSize ().

6. Håndtag nul Værdier

Lad os nu se, hvordan man håndterer cache nul værdier. Som standard, Guava-cache kaster undtagelser, hvis du prøver at indlæse en nul værdi - da det ikke giver mening at cache a nul.

Men hvis nul værdi betyder noget i din kode, så kan du gøre god brug af Valgfri klasse som i følgende eksempel:

@Test offentlig ugyldig nårNullValue_thenOptional () {CacheLoader læsser loader = ny CacheLoader() {@Override public Optional load (String key) {return Optional.fromNullable (getSuffix (key)); }}; LoadingCache cache; cache = CacheBuilder.newBuilder (). build (loader); assertEquals ("txt", cache.getUnchecked ("text.txt"). get ()); assertFalse (cache.getUnchecked ("hej"). isPresent ()); } privat streng getSuffix (endelig strengstr) {int lastIndex = str.lastIndexOf ('.'); hvis (lastIndex == -1) {return null; } returnere str.substring (lastIndex + 1); }

7. Opdater cachen

Lad os derefter se, hvordan vi opdaterer vores cacheværdier.

7.1. Manuel opdatering

Vi kan opdatere en enkelt nøgle manuelt ved hjælp af LoadingCache.refresh (nøgle).

Strengværdi = loadingCache.get ("nøgle"); loadingCache.refresh ("nøgle");

Dette vil tvinge CacheLoader for at indlæse den nye værdi for nøgle.

Indtil den nye værdi er indlæst, den tidligere værdi af nøgle vil blive returneret af få (nøgle).

7.2. Automatisk opdatering

Vi kan bruge CacheBuilder.refreshAfterWrite (varighed) for automatisk at opdatere cachelagrede værdier.

@Test offentlig ugyldig nårLiveTimeEnd_thenRefresh () {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder () .refreshAfterWrite (1, TimeUnit.MINUTES) .build (loader); }

Det er vigtigt at forstå det refreshAfterWrite (varighed) laver kun en nøgle berettiget til opdateringen efter den angivne varighed. Værdien opdateres faktisk kun, når en tilsvarende post forespørges af få (nøgle).

8. Indlæs cachen på forhånd

Vi kan indsætte flere poster i vores cache ved hjælp af putAll () metode. I det følgende eksempel tilføjer vi flere poster i vores cache ved hjælp af a Kort:

@Test offentlig ugyldig nårPreloadCache_thenUsePutAll () {CacheLoader loader; loader = ny CacheLoader () {@Override public String load (String key) {return key.toUpperCase (); }}; LoadingCache cache; cache = CacheBuilder.newBuilder (). build (loader); Kortkort = nyt HashMap (); map.put ("første", "FØRSTE"); map.put ("second", "SECOND"); cache.putAll (kort); assertEquals (2, cache.size ()); }

9. FjernelseNotifikation

Nogle gange er du nødt til at tage nogle handlinger, når en post fjernes fra cachen; så lad os diskutere Fjernelse Meddelelse.

Vi kan registrere en FjernelseLytter for at få beskeder om, at en post fjernes. Vi har også adgang til årsagen til fjernelsen - via getCause () metode.

I den følgende prøve er a Fjernelse Meddelelse modtages, når det fjerde element i cachen på grund af dets størrelse:

@Test offentlig ugyldig nårEntryRemovedFromCache_thenNotify () {CacheLoader loader; loader = ny CacheLoader () {@Override offentlig strengbelastning (sidste strengnøgle) {return key.toUpperCase (); }}; RemovalListener lytter; listener = new RemovalListener () {@Override public void onRemoval (RemovalNotification n) {if (n.wasEvicted ()) {String cause = n.getCause (). name (); assertEquals (RemovalCause.SIZE.toString (), årsag); }}} LoadingCache cache; cache = CacheBuilder.newBuilder () .maximumSize (3) .removalListener (lytter) .build (loader); cache.getUnchecked ("første"); cache.getUnchecked ("anden"); cache.getUnchecked ("tredje"); cache.getUnchecked ("sidste"); assertEquals (3, cache.size ()); }

10. Noter

Endelig er her et par yderligere hurtige noter om implementeringen af ​​Guava-cache:

  • det er trådsikkert
  • du kan indsætte værdier manuelt i cachen ved hjælp af put (nøgle, værdi)
  • du kan måle din cache-ydeevne ved hjælp af CacheStats ( hitRate (), missRate (), ..)

11. Konklusion

Vi gennemgik mange brugssager af Guava Cache i denne vejledning - fra enkel brug til udsendelse af elementer, opdatering og forudindlæsning af cachen og meddelelser om fjernelse.

Som sædvanligt kan alle eksemplerne findes på GitHub.