1. Historien omLinkedList
Java har en annen samlingsklasse Java arvet fra C++-språket. Dette er LinkedList
klassen som implementerer en "lenket liste".
Utad ser a LinkedList
ut til å være det samme som en ArrayList
. Klassen LinkedList
har alle de samme metodene som ArrayList
klassen. I prinsippet kan du alltid bruke en LinkedList
i stedet for en ArrayList
og alt vil fungere.
Så hvorfor trenger vi en annen listeklasse?
Svaret har alt å gjøre med den interne strukturen i LinkedList
klassen. I stedet for en matrise bruker den en dobbeltlenket liste . Vi vil forklare hva det er litt senere.
Klassens LinkedList
ulike interne struktur gjør den raskest til å sette inn elementer midt på listen.
På Internett kan du ofte finne sammenligninger av klassene ArrayList
og LinkedList
:
Operasjon | Metode | ArrayList | LinkedList |
---|---|---|---|
Legg til et element |
|
Fort | Veldig fort |
Sett inn et element |
|
Langsom | Veldig fort |
Få et element |
|
Veldig fort | Langsom |
Sett et element |
|
Veldig fort | Langsom |
Fjern et element |
|
Langsom | Veldig fort |
Alt virker klart nok: hvis du trenger å sette inn elementer i listen ofte, bruk LinkedList
; hvis sjelden, bruk ArrayList. Men virkeligheten er litt annerledes.
2. Ingen brukerLinkedList
Ingen bruker LinkedList
.
Til og med forfatteren av klassen LinkedList
twitret nylig: "Bruker noen faktisk LinkedList
? Jeg skrev det, og jeg bruker det aldri."
Så hva er greia?
Først begynte klassen ArrayList
veldig raskt å kunne sette inn elementer midt på listen. Når du setter inn et element i midten av listen, må du flytte alle elementene etter innsettingspunktet med 1 mot slutten av listen. Dette pleide å ta tid.
Men nå har alt endret seg. Alle elementene i en matrise er nær hverandre i den samme minneblokken, så operasjonen for å skifte elementene utføres av en veldig rask lavnivåkommando: .System.arraycopy()
I tillegg har dagens prosessorer en stor cache som vanligvis kan inneholde hele arrayet, noe som gjør at array-elementene kan flyttes inne i cachen i stedet for i minnet. En million elementer forskyves lett på et enkelt millisekund.
For det andre kan klassen LinkedList
sette inn elementer raskt hvis du setter dem inn ved hjelp av en iterator. Hvis du bruker en iterator for å gå gjennom en LinkedList
og hele tiden sette inn nye elementer (eller fjerne eksisterende), er operasjonen superrask.
Hvis du ganske enkelt legger til elementer til et LinkedList
objekt inne i en løkke, blir hver hurtiginnsettingsoperasjon ledsaget av en langsom "hent element"-operasjon.
Virkeligheten er mye nærmere dette:
Operasjon | Metode | ArrayList | LinkedList |
---|---|---|---|
Legg til et element |
|
Fort | Veldig fort |
Sett inn et element |
|
Langsom | Veldig treg |
Få et element |
|
Veldig fort | Veldig treg |
Sett et element |
|
Veldig fort | Veldig treg |
Fjern et element |
|
Langsom | Veldig treg |
Sett inn ved hjelp av en iterator |
|
Langsom | Veldig fort |
Fjern ved hjelp av en iterator |
|
Langsom | Veldig fort |
LinkedList
Hvorfor er det så treg å få et element fra en operasjon?
Du kan svare på det spørsmålet etter å ha satt deg litt inn i hvordan LinkedList
er strukturert.
3. Hvordan LinkedList
er strukturert
LinkedList
har en annen intern struktur enn ArrayList
. Den har ikke en intern matrise for lagring av elementer. I stedet bruker den en datastruktur som kalles en liste med dobbelt blekk .
Hvert element i en dobbeltlenket liste lagrer en referanse til det forrige elementet og det neste elementet. Dette er litt som en linje i en butikk, der hver person husker personen som står foran dem, så vel som personen som står bak dem.
Slik ser en slik liste ut i minnet:
Hodet og halen (celler med grå bakgrunn) er variablene first
og last
, som lagrer referanser til Node
objekter.
I midten har du en kjede av Node
objekter (objekter, ikke variabler). Hver av dem består av tre felt:
prev
— lagrer en referanse (lenke) til forrigeNode
objekt (celler med gul bakgrunn).value
— lagrer verdien til dette elementet i listen (celler med grønn bakgrunn).next
— lagrer en referanse (lenke) til nesteNode
objekt (celler med blå bakgrunn)
Det andre objektet (adresse F24) er det neste ( next
) for det første objektet og det forrige ( prev
) for det tredje objektet. Det gule feltet til det tredje objektet inneholder adressen F24 og det blå feltet til det første objektet inneholder adressen F24.
Pilene fra det første og tredje objektet peker til det samme andre objektet. Så det ville vært mer riktig å tegne pilene slik.
4. Sett inn et element i en koblet liste
For å legge til noen i linjen som dette, trenger du bare å få tillatelse til to personer som står ved siden av hverandre. Den første personen husker nykommeren som "personen bak meg", og den andre personen husker dem som "personen foran meg".
Alt du trenger å gjøre er å endre referansene til de to naboobjektene:
Vi la til et nytt element i listen vår ved å endre referansene til det andre og tredje objektet. Det nye objektet er det neste for det gamle andre objektet og det forrige for det gamle tredje objektet. Og selvfølgelig må det nye objektet selv lagre de riktige referansene: dets forrige objekt er det gamle andre objektet, og det neste objektet er det gamle tredje objektet.
Å fjerne et element er enda enklere: Hvis vi ønsker å fjerne, la oss si, det 100. objektet fra listen, trenger vi bare å endre feltet next
for det 99. objektet slik at det peker til det 101. objektet, og endre feltet prev
for det 101. objekt slik at det peker mot 99. Det er det.
Men å få det 100. objektet er ikke så lett.
5. Fjern et element fra en liste
For å få det 100. elementet i en koblet liste, må du:
Hent 1. objekt: Dette gjør du ved å bruke first
variabelen i LinkedList
objektet. Feltet next
til det 1. objektet lagrer en referanse til det 2. objektet. Slik får vi det andre objektet. Det andre objektet har en referanse til det tredje, og så videre.
Hvis vi trenger å få en referanse til det 100. objektet, må vi sekvensielt bevege oss gjennom alle objektene fra det 1. til det 100. Og hvis vi trenger det millionte elementet i en liste, må vi iterere over en million objekter etter hverandre!
Og hvis disse objektene ble lagt til listen på forskjellige tidspunkter, vil de være plassert i forskjellige deler av minnet og vil neppe havne i prosessorens cache samtidig. Dette betyr at det å iterere over elementene i en koblet liste ikke bare er sakte, men veldig sakte.
Det er det vi har med å gjøre.
Så hvorfor lærer vi deg hvordan dette trege LinkedList
fungerer?
Vel, under jobbintervjuer vil du bli spurt om hvordan LinkedList
forskjellen er fraArrayList
. Helt sikkert.
GO TO FULL VERSION