Een gids voor LinkedHashMap in Java

1. Overzicht

In dit artikel gaan we de interne implementatie van LinkedHashMap klasse. LinkedHashMap is een veel voorkomende implementatie van Kaart koppel.

Deze specifieke implementatie is een subklasse van Hash kaart en deelt daarom de kernbouwstenen van de Hash kaart implementatie. Daarom wordt het ten zeerste aanbevolen om dat op te frissen voordat u verder gaat met dit artikel.

2. LinkedHashMap vs Hash kaart

De LinkedHashMap klasse lijkt erg op Hash kaart in de meeste aspecten. De gekoppelde hash-map is echter gebaseerd op zowel de hash-tabel als de gekoppelde lijst om de functionaliteit van de hash-map te verbeteren.

Het onderhoudt een dubbel gekoppelde lijst die door al zijn vermeldingen loopt, naast een onderliggende array van standaardgrootte 16.

Om de volgorde van de elementen te behouden, wijzigt de gekoppelde hashmap de Kaart klas van Hash kaart door verwijzingen toe te voegen aan de volgende en vorige items:

static class Entry breidt HashMap.Node {Entry before, after; Entry (int hash, K key, V value, Node next) {super (hash, key, value, next); }}

Merk op dat de Binnenkomst klasse voegt eenvoudig twee aanwijzingen toe; voordat en na waardoor het zichzelf kan koppelen aan de gekoppelde lijst. Afgezien daarvan gebruikt het de Binnenkomst klasse implementatie van een de HashMap.

Onthoud ten slotte dat deze gekoppelde lijst de volgorde van iteratie definieert, wat standaard de volgorde van invoeging van elementen is (invoegvolgorde).

3. Invoegopdracht LinkedHashMap

Laten we eens kijken naar een gekoppelde hash-kaartinstantie die de vermeldingen rangschikt op basis van hoe ze in de kaart zijn ingevoegd. Het garandeert ook dat deze volgorde wordt gehandhaafd gedurende de hele levenscyclus van de kaart:

@Test openbare ongeldig gegevenLinkedHashMap_whenGetsOrderedKeyset_thenCorrect () {LinkedHashMap map = nieuwe LinkedHashMap (); map.put (1, null); map.put (2, null); map.put (3, null); map.put (4, null); map.put (5, null); Stel sleutels in = map.keySet (); Geheel getal [] arr = keys.toArray (nieuw geheel getal [0]); for (int i = 0; i <arr.length; i ++) {assertEquals (new Integer (i + 1), arr [i]); }}

Hier maken we gewoon een rudimentaire, niet-sluitende test over de volgorde van items in de gekoppelde hash-kaart.

We kunnen garanderen dat deze test altijd zal slagen, aangezien de plaatsingsvolgorde altijd wordt gehandhaafd. We kunnen niet dezelfde garantie geven voor een HashMap.

Dit attribuut kan van groot voordeel zijn in een API die elke kaart ontvangt, een kopie maakt om te manipuleren en deze terugstuurt naar de aanroepende code. Als de klant de geretourneerde kaart op dezelfde manier moet ordenen voordat hij de API aanroept, dan is een gekoppelde hashmap de juiste keuze.

De invoegvolgorde wordt niet beïnvloed als een sleutel opnieuw in de kaart wordt geplaatst.

4. Toegangsvolgorde LinkedHashMap

LinkedHashMap biedt een speciale constructor die ons in staat stelt om onder de aangepaste belastingsfactor (LF) en initiële capaciteit te specificeren, een ander ordeningsmechanisme / strategie genaamd access-order:

LinkedHashMap map = nieuwe LinkedHashMap (16, .75f, true);

De eerste parameter is de initiële capaciteit, gevolgd door de belastingsfactor en de laatste parameter is de bestelmodus. Dus door langs te gaan waar, hebben we access-order ingeschakeld, terwijl de standaard insertion-order was.

Dit mechanisme zorgt ervoor dat de volgorde van iteratie van elementen de volgorde is waarin de elementen het laatst zijn geopend, van de minst recent geopende tot de meest recent geopende.

En dus is het bouwen van een Least Recent Used (LRU) cache vrij eenvoudig en praktisch met dit soort kaarten. Een succesvolle leggen of krijgen bewerking resulteert in een toegang voor het item:

@Test openbare ongeldig gegevenLinkedHashMap_whenAccessOrderWorks_thenCorrect () {LinkedHashMap map = nieuwe LinkedHashMap (16, .75f, true); map.put (1, null); map.put (2, null); map.put (3, null); map.put (4, null); map.put (5, null); Stel sleutels in = map.keySet (); assertEquals ("[1, 2, 3, 4, 5]", keys.toString ()); map.get (4); assertEquals ("[1, 2, 3, 5, 4]", keys.toString ()); map.get (1); assertEquals ("[2, 3, 5, 4, 1]", keys.toString ()); map.get (3); assertEquals ("[2, 5, 4, 1, 3]", keys.toString ()); }

Merk op hoe de volgorde van de elementen in de sleutelset wordt getransformeerd terwijl we toegangsbewerkingen op de kaart uitvoeren.

Simpel gezegd, elke toegangsbewerking op de kaart resulteert in een zodanige volgorde dat het element waartoe toegang werd verkregen als laatste zou verschijnen als er meteen een iteratie zou worden uitgevoerd.

Na de bovenstaande voorbeelden zou het duidelijk moeten zijn dat a zet alle operatie genereert één toegangstoegang voor elk van de mappings in de gespecificeerde map.

Uiteraard heeft iteratie over een weergave van de kaart geen invloed op de volgorde van iteratie van de achtergrondkaart; alleen expliciete toegangsbewerkingen op de kaart hebben invloed op de volgorde.

LinkedHashMap biedt ook een mechanisme om een ​​vast aantal toewijzingen te behouden en om de oudste vermeldingen te blijven verwijderen voor het geval er een nieuwe moet worden toegevoegd.

De removeEldestEntry methode kan worden overschreven om dit beleid af te dwingen voor het automatisch verwijderen van verouderde toewijzingen.

Laten we, om dit in de praktijk te zien, onze eigen gekoppelde hash-mapklasse maken, met als enig doel het verwijderen van verouderde toewijzingen af ​​te dwingen door uit te breiden LinkedHashMap:

openbare klasse MyLinkedHashMap breidt LinkedHashMap uit {privé statische finale int MAX_ENTRIES = 5; openbare MyLinkedHashMap (int initialCapacity, float loadFactor, boolean accessOrder) {super (initialCapacity, loadFactor, accessOrder); } @Override beschermde boolean removeEldestEntry (Map.Entry oudste) {return size ()> MAX_ENTRIES; }}

Met onze overschrijving hierboven kan de kaart uitgroeien tot een maximale grootte van 5 items. Wanneer de grootte groter is, wordt elk nieuw item ingevoegd ten koste van het verlies van het oudste item op de kaart, d.w.z. het item waarvan de laatste toegangstijd voorafgaat aan alle andere items:

@Test openbare ongeldig gegevenLinkedHashMap_whenRemovesEldestEntry_thenCorrect () {LinkedHashMap map = nieuwe MyLinkedHashMap (16, .75f, true); map.put (1, null); map.put (2, null); map.put (3, null); map.put (4, null); map.put (5, null); Stel sleutels in = map.keySet (); assertEquals ("[1, 2, 3, 4, 5]", keys.toString ()); map.put (6, null); assertEquals ("[2, 3, 4, 5, 6]", keys.toString ()); map.put (7, null); assertEquals ("[3, 4, 5, 6, 7]", keys.toString ()); map.put (8, null); assertEquals ("[4, 5, 6, 7, 8]", keys.toString ()); }

Merk op hoe de oudste vermeldingen aan het begin van de sleutelset steeds minder worden als we nieuwe toevoegen aan de kaart.

5. Prestatieoverwegingen

Net als Hash kaart, LinkedHashMap voert de basis uit Kaart bewerkingen van toevoegen, verwijderen en bevatten in constante tijd, zolang de hash-functie goed gedimensioneerd is. Het accepteert ook een null-sleutel en null-waarden.

Dit constante prestaties van LinkedHashMap is waarschijnlijk een beetje erger dan de constante tijd van Hash kaart vanwege de extra overhead van het bijhouden van een dubbelgekoppelde lijst.

Herhaling van collectie-weergaven van LinkedHashMap kost ook lineaire tijd Aan) vergelijkbaar met dat van Hash kaart. Aan de andere kant,LinkedHashMapDe lineaire tijdprestaties tijdens iteratie zijn beter dan Hash kaart‘S lineaire tijd.

Dit komt omdat, voor LinkedHashMap, n in Aan) is alleen het aantal vermeldingen op de kaart, ongeacht de capaciteit. Terwijl, voor Hash kaart, n is de capaciteit en de omvang samengevat, O (maat + capaciteit).

Belastingsfactor en initiële capaciteit worden precies gedefinieerd zoals voor Hash kaart. Merk echter op dat de boete voor het kiezen van een te hoge waarde voor initiële capaciteit is minder zwaar voor LinkedHashMap dan voor Hash kaart, aangezien iteratietijden voor deze klasse niet worden beïnvloed door capaciteit.

6. Gelijktijdigheid

Net als Hash kaart, LinkedHashMap implementatie is niet gesynchroniseerd. Dus als u het wilt openen vanuit meerdere threads en ten minste één van deze threads zal het waarschijnlijk structureel veranderen, dan moet het extern worden gesynchroniseerd.

U kunt dit het beste doen bij het maken:

Map m = Collections.synchronizedMap (nieuwe LinkedHashMap ());

Het verschil met Hash kaart ligt in wat een structurele wijziging inhoudt. In door toegang geordende gekoppelde hash-kaarten, hoeft u alleen maar de krijgen API resulteert in een structurele wijziging. Daarnaast zijn operaties zoals leggen en verwijderen.

7. Conclusie

In dit artikel hebben we Java verkend LinkedHashMap class als een van de belangrijkste implementaties van Kaart interface in termen van gebruik. We hebben ook de interne werking onderzocht in termen van het verschil met Hash kaart dat is zijn superklasse.

Hopelijk kunt u na het lezen van dit bericht beter geïnformeerde en effectieve beslissingen nemen over welke kaartimplementatie u in uw use-case moet gebruiken.

De volledige broncode voor alle voorbeelden die in dit artikel worden gebruikt, is te vinden in het GitHub-project.