FIFO Cache (First In First Out Cache) — en cachelagringsalgoritm där det tidigast tillagda elementet avlägsnas, oavsett hur ofta det har använts. Implementeras via en kö: nya element läggs till i svansen och vid överflöd tas elementet från huvudet bort. Enligt Android Developers (2026) ger FIFO Cache O(1) för alla operationer men ligger efter LRU i träfffrekvens vid ojämna åtkomstmönster till data.
Huvudpunkter
FIFO Cache (First In First Out Cache) — en cache med fast storlek som använder en kö för att hantera element. Det första tillagda elementet finns i kön huvudet och kommer att tas bort först vid överflöd. Nya element läggs alltid till i svansen, vilket garanterar att borttagningsordningen överensstämmer med tilläggsordningen.
Till skillnad från LRU, som omordnar element vid varje åtkomst, ändrar FIFO inte positionen för befintliga element vid get-operationer. Detta gör algoritmen helt deterministisk: genom att känna till tilläggsordningen kan man exakt förutsäga vilket element som kommer att tas bort härnäst. En sådan förutsägbarhet är kritisk för realtidssystem där databehandling måste garanteras i ankomstordning.
Implementeringen av FIFO Cache kan byggas på flera datastrukturer: cirkulär buffert (circular buffer) för maximal prestanda, länkad lista för flexibilitet eller två stackar (Two-Stack Queue) för språk utan inbyggd kö. Den cirkulära bufferten ger bäst cache-lokalitet och minimal överhead, men kräver förhandsallokering av minne för maxSize.
Operationen enqueue(value) lägger till ett element i slutet av kön. Om storleken har nått maxSize tas elementet från huvudet bort före tilläggning. Operationen dequeue() tar bort och returnerar elementet från huvudet — för tvungen hämtning av det äldsta elementet. Operationen peek() returnerar elementet från huvudet utan borttagning — för att visa det äldsta elementet utan att ändra kön.
FIFO-algoritmen efterliknar beteendet hos en vanlig kö: först in, först ut. I cachelagringssammanhang innebär detta att elementet som varit längst i cachen tas bort vid platsbrist — oavsett hur efterfrågat det är. Borttagningspolicyn för FIFO ignorerar åtkomstfrekvensen, vilket är både en styrka och en svaghet hos algoritmen.
Vid implementering via cirkulär buffert används två pekare: head (index för kön huvud) och tail (index för kön svans). Vid enqueue skrivs elementet på tail-index och tail ökas. Om tail når buffertens storlek lindas den tillbaka till början av arrayen. Om tail kommer ikapp head — är kön full och head flyttas (borttagning). Den cirkulära bufferten kräver inte dynamisk minnesallokering och undviker fragmentering.
FIFO Cache uppvisar en träfffrekvens på 40% till 60% för typiska belastningar, vilket är högre än LIFO men lägre än LRU. För scenarier där dataåtkomsten är enhetlig och det inte finns några heta punkter kan FIFO dock visa resultat jämförbara med LRU med betydligt lägre implementeringskomplexitet. Minnet används effektivt: inga extra pekare behövs för omordning av element.
Den största nackdelen med FIFO — mottaglighet för cache-förorening (cache pollution). Om en stor mängd data som aldrig kommer att behövas igen läggs till i cachen kommer de gradvis att ta bort alla användbara element och träfffrekvensen sjunker dramatiskt. LRU löser delvis detta problem eftersom ofta använda element ständigt uppdateras genom att flyttas till huvudet, medan engångselement tas bort snabbare. I FIFO förblir engångsdata i cachen tills de tas bort i köns naturliga ordning.
Valet mellan FIFO, LRU och LIFO beror på dataåtkomstmönstret och kraven på beteendeförutsägbarhet. LRU är optimalt för de flesta scenarier, FIFO — för strömmande data med enhetlig åtkomst, LIFO — för stackstrukturer.
| Parameter | FIFO | LRU | LIFO |
|---|---|---|---|
| Borttagningskriterium | Först tillagd | Minst nyligen använd | Senast tillagd |
| Struktur | Kö | HashMap + Doubly Linked List | Stack |
| Förutsägbarhet | Hög | Medel | Hög |
| Skydd mot förorening | Låg | Medel | Låg |
| Strömmande data | Utmärkt | Godtagbar | Dålig |
| Resurser (CPU/RAM) | Minimum | Medel | Minimum |
FIFO är idealiskt för scenarier där bearbetningsordningen måste överensstämma med ankomstordningen: databuffring, loggning, händelsebearbetning. LRU är bättre för cachelagring med ojämn åtkomst (användardata). LIFO är endast tillämpligt för stackar och Ångra-operationer. För de flesta mobilapplikationer förblir LRU standardvalet, men FIFO kan vara att föredra vid strikta minnesbegränsningar eller krav på förutsägbarhet.
FIFO Cache används i scenarier där förutsägbarhet vid borttagning eller databehandlingsordning är viktig. Låt oss titta på de viktigaste användningsfallen.
Vid uppspelning av ljud och video kommer data i en kontinuerlig ström och lagras tillfälligt i en buffert. FIFO Cache säkerställer att de första mottagna fragmenten skickas först till avkodning — detta garanterar smidig uppspelning utan fördröjning. Buffertstorleken väljs baserat på strömmens bithastighet och tillåten fördröjning: för ljud typiskt 2–5 sekunder, för video — 10–30 sekunder. FIFO är idealiskt för sådana scenarier eftersom omordning av data (som i LRU) inte är meningsfull.
Vid begränsning av antalet samtidiga nätverksförfrågningar kan FIFO Cache användas för att lagra väntande förfrågningar. Den första tillagda förfrågan kommer att utföras först, vilket säkerställer rättvis fördelning av nätverksresurser mellan olika komponenter i applikationen. Denna metod används i OkHttp Dispatcher och liknande bibliotek för hantering av anslutningspooler.
Enkla cacheminnen för HTTP-svar på mobila enheter använder ofta FIFO. Svar på förfrågningar lagras i ankomstordning och när gränsen nås tas de äldsta bort. Även om LRU skulle ge bättre träfffrekvens för användarscenarier är FIFO enklare att implementera och kräver inte lagring av tid för senaste åtkomst för varje svar. För API med enhetlig belastning är skillnaden i träfffrekvens mellan FIFO och LRU minimal.
I mobilapplikationer buffras beröringshändelser (touch events) i en FIFO-kö före gestigenkänning. Varje händelse måste bearbetas i händelseordning, annars kommer gesten att kännas igen felaktigt. FIFO Cache med storleksbegränsning förhindrar buffertspill vid snabba svepningar genom att kassera de äldsta händelserna om applikationen inte hinner bearbeta dem.
Låt oss titta på implementeringen av FIFO Cache i Kotlin med cirkulär buffert — den mest prestandaeffektiva metoden för mobila enheter.
class FifoCache<V>(
private val maxSize: Int
) {
private val buffer = arrayOfNulls<V>(maxSize)
private var head = 0
private var tail = 0
private var size = 0
fun enqueue(value: V) {
if (size == maxSize) {
// ta bort äldsta elementet
buffer[head] = null
head = (head + 1) % maxSize
size--
}
buffer[tail] = value
tail = (tail + 1) % maxSize
size++
}
fun dequeue(): V? {
if (size == 0) return null
val result = buffer[head]
buffer[head] = null
head = (head + 1) % maxSize
size--
return result
}
fun peek(): V? {
return buffer[head]
}
}
Den cirkulära bufferten använder indexen head och tail, som cykliskt ökas modulo maxSize. När size == maxSize tar enqueue först bort elementet vid head (äldst), flyttar head och skriver sedan det nya elementet vid tail. Modulär aritmetik lindar automatiskt pekarna tillbaka till början av arrayen, vilket eliminerar manuell kopiering av data.
I Swift är ett bekvämt alternativ — FIFO-kö baserad på två stackar (Two-Stack Queue). Alla enqueue utförs i den första stacken (push), och vid dequeue överförs elementen till den andra stacken i omvänd ordning — så dequeue-operationen blir i genomsnitt O(1).
struct FifoCache<Value> {
private let maxSize: Int
private var inStack = [Value]()
private var outStack = [Value]()
mutating func enqueue(value: Value) {
if inStack.count + outStack.count >= maxSize {
if outStack.isEmpty {
outStack = inStack.reversed()
inStack.removeAll()
}
outStack.removeLast()
}
inStack.append(value)
}
mutating func dequeue() -> Value? {
if outStack.isEmpty {
outStack = inStack.reversed()
inStack.removeAll()
}
return outStack.popLast()
}
}
Två stackar ger amorterad komplexitet O(1) för enqueue och dequeue. outStack.removeLast() vid borttagning tar bort det äldsta elementet (först tillagt). Denna metod kräver inte förhandsallokering av minne men kan skapa extra belastning på sophämtaren vid frekventa stackomkastningar. För mobila applikationer med begränsat minne är den cirkulära bufferten fortfarande att föredra.
Vanliga frågor
En kö är en abstrakt datastruktur utan storleksbegränsning. FIFO Cache är en kö med fast maximal storlek och borttagningspolicy: vid överflöd tas elementet från huvudet automatiskt bort. En vanlig kö blockerar tilläggning vid överflöd eller expanderar dynamiskt, medan FIFO Cache alltid accepterar ny data genom att ta bort gammal.
FIFO är bättre än LRU i scenarier med enhetlig dataåtkomst där det inte finns några heta punkter. Till exempel vid cachelagring av loggfiler eller strömmande data används varje värde en gång och LRU ger ingen fördel. FIFO är också att föredra vid strikta minnesbegränsningar — det kräver inga extra pekare för omordningar, vilket sparar 16+ byte per element.
På Android kan man använda ArrayDeque från Kotlin standardbibliotek, som implementerar en cirkulär buffert. För FIFO Cache, omslut ArrayDeque: vid enqueue kontrollera storleken och vid överskridande anropa removeFirst(). För en trådsäker version, använd ConcurrentLinkedDeque eller SynchronizedArrayDeque.
Om en stor mängd engångsdata läggs till i cachen kommer de att ta bort alla användbara element. Till exempel, laddning av 50 bilder för ett galleri vid maxSize=30 kommer att ta bort de första 20 användbara bilderna, även om användaren troligen kommer att återvända till dem. LRU löser delvis detta problem: ofta använda element uppdateras och förblir i cachen.
Ja, det finns hybridalgoritmer. 2Q (Two-Queue) delar cachen i två delar: het (LRU) och kall (FIFO). Nya element kommer först in i FIFO-kön och endast upprepad åtkomst flyttar dem till LRU-delen. Detta skyddar LRU från förorening av engångsdata samtidigt som hög träfffrekvens bibehålls för ofta använda element.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också