Datenstrukturen im Überblick
Datenstrukturen bestimmen, wie deine Android-App Daten hält, findet und verändert. Du lernst Arrays, Listen, Maps und Sets gezielt einzuordnen.
Wenn du Android-Apps baust, arbeitest du ständig mit Daten: Ein Warenkorb enthält Produkte, ein Chat zeigt Nachrichten, ein Formular merkt sich ausgewählte Optionen, ein Cache ordnet IDs passenden Objekten zu. Datenstrukturen sind die Werkzeuge, mit denen du solche Informationen im Speicher organisierst. Deine Wahl beeinflusst, wie leicht dein Code zu verstehen ist, wie schnell deine App reagiert und wie zuverlässig Zustände in Kotlin, Jetpack Compose und deiner Architektur fließen.
Was ist das?
Eine Datenstruktur beschreibt, wie Daten angeordnet, gespeichert und wiedergefunden werden. Für deinen Einstieg sind vier Gruppen besonders wichtig: Arrays, Lists, Maps und Sets. Sie lösen ähnliche, aber nicht gleiche Probleme.
Ein Array ist eine feste Folge gleichartiger Werte. Du greifst über einen Index darauf zu, zum Beispiel auf Element 0, 1 oder 2. In Kotlin siehst du Arrays eher dann, wenn du mit APIs arbeitest, die genau diese Form erwarten, oder wenn feste Größe und kompakte Speicherung wichtig sind.
Eine List ist ebenfalls geordnet, aber im Alltag flexibler. Kotlin unterscheidet zwischen List<T> für eine lesende Sicht und MutableList<T> für veränderbare Listen. In Android begegnen dir Listen überall: Ein LazyColumn in Compose zeigt eine Liste von UI-Modellen, ein Repository liefert eine Liste von Einträgen aus einer Datenbank, ein ViewModel hält eine Liste im UI-State.
Eine Map ordnet Schlüssel zu Werten zu. Statt eine Liste nach einer bestimmten ID zu durchsuchen, fragst du direkt map[id] ab. Das passt sehr gut zu typischen App-Daten: Nutzer-ID zu Nutzerprofil, Produkt-ID zu Warenkorbposition, Feldname zu Fehlermeldung.
Ein Set ist eine Sammlung eindeutiger Werte. Es ist hilfreich, wenn nicht die Reihenfolge im Vordergrund steht, sondern die Frage: Ist dieses Element enthalten oder nicht? Beispiele sind ausgewählte Filter, bereits geladene IDs oder Berechtigungen.
Das mentale Modell ist: Frage zuerst, welche Operation deine App häufig braucht. Musst du Reihenfolge anzeigen? Nimm meist eine List. Musst du schnell über eine ID finden? Denke an eine Map. Musst du Duplikate vermeiden? Ein Set ist oft sauberer. Hast du eine feste, indexbasierte Struktur? Ein Array kann passen.
Wie funktioniert es?
Datenstrukturen unterscheiden sich vor allem bei Zugriff, Änderung, Suche und Eindeutigkeit. Diese Unterschiede wirken sich direkt auf App-Verhalten und Performance aus. Die Android-Performance-Dokumentation betont allgemein, dass Apps sparsam mit Arbeit, Speicher und unnötigen Operationen umgehen sollen. Datenstrukturen sind dafür ein früher Hebel, weil sie bestimmen, welche Arbeit dein Code immer wieder leisten muss.
Bei einer List ist der Zugriff per Index schnell: items[3] ist direkt erreichbar. Die Suche nach einem bestimmten Objekt kann aber teuer werden, weil die Liste im Normalfall Element für Element geprüft wird. Wenn du in jedem Compose-Recompose-Durchlauf eine große Liste nach einer ID durchsuchst, kann das spürbar werden. Nicht jede kleine Liste ist ein Problem, aber wiederholte lineare Suche in heißen Pfaden ist ein Warnsignal.
Eine Map ist für Schlüsselzugriff gebaut. Wenn du häufig userId hast und dazu das passende User-Objekt brauchst, ist Map<UserId, User> oft klarer und schneller als users.first { it.id == userId }. Der Preis ist, dass du einen sinnvollen Schlüssel brauchst und die Daten beim Aufbau der Map korrekt halten musst. Eine Map ist also kein Ersatz für jedes Modell, sondern ein Werkzeug für gezielten Zugriff.
Ein Set prüft Zugehörigkeit sehr direkt. Bei selectedIds.contains(id) drückt dein Code genau aus, was gemeint ist. Mit einer List würdest du zwar ebenfalls contains schreiben können, aber fachlich bleibt unklar, ob Duplikate erlaubt sind. Ein Set macht diese Regel sichtbar: Jeder Wert kommt nur einmal vor.
Arrays sind in Kotlin weniger bequem als Listen, aber sie bleiben wichtig. Array<T> speichert Objektreferenzen, während spezialisierte Varianten wie IntArray primitive Zahlen effizienter halten. Du musst jedoch beachten, dass Arrays eine feste Länge haben. Wenn du ständig Elemente hinzufügst oder entfernst, ist eine MutableList meist passender.
In moderner Android-Architektur liegen diese Strukturen oft im UI-State. Ein ViewModel kann etwa einen unveränderlichen State mit List<MessageUiModel> halten. Wenn sich Daten ändern, erzeugst du eine neue Liste und veröffentlichst einen neuen State. Das passt gut zu Compose, weil Änderungen klar erkennbar sind. Mutierst du dagegen eine alte MutableList heimlich weiter, kann die UI veraltete oder schwer nachvollziehbare Zustände zeigen.
In der Praxis
Stell dir vor, du baust eine Produktliste mit Favoriten. Die UI zeigt alle Produkte in Reihenfolge. Gleichzeitig muss jedes Produkt wissen, ob es favorisiert ist. Eine reine Liste für alles ist möglich, wird aber schnell unübersichtlich. Eine gute Aufteilung ist: List<Product> für die Anzeige, Set<String> für favorisierte Produkt-IDs und bei Bedarf Map<String, Product> für schnellen Zugriff auf Details.
data class Product(
val id: String,
val name: String,
val priceLabel: String
)
data class ProductUiModel(
val id: String,
val title: String,
val priceLabel: String,
val isFavorite: Boolean
)
fun buildProductUiModels(
products: List<Product>,
favoriteIds: Set<String>
): List<ProductUiModel> {
return products.map { product ->
ProductUiModel(
id = product.id,
title = product.name,
priceLabel = product.priceLabel,
isFavorite = product.id in favoriteIds
)
}
}
fun indexProductsById(products: List<Product>): Map<String, Product> {
return products.associateBy { it.id }
}
Hier hat jede Datenstruktur eine klare Aufgabe. Die List<Product> bewahrt die Reihenfolge, in der du Produkte anzeigen willst. Das Set<String> sagt eindeutig, welche IDs favorisiert sind. Die Map<String, Product> ist nützlich, wenn du später aus einer ID schnell ein Produkt holen möchtest, etwa beim Öffnen einer Detailseite.
Eine praktische Entscheidungsregel lautet: Verwende die Datenstruktur, die deine fachliche Absicht direkt ausdrückt. Wenn du eindeutige Auswahlwerte meinst, nutze ein Set. Wenn du geordnete UI-Elemente renderst, nutze eine List. Wenn du Lookup nach Schlüssel brauchst, nutze eine Map. Wenn eine API ein Array verlangt oder du mit festen Daten arbeitest, nutze ein Array.
Eine typische Stolperfalle ist unnötige Wiederholung von Arbeit. Dieser Code sieht harmlos aus:
val uiModels = products.map { product ->
val isFavorite = favorites.firstOrNull { it.id == product.id } != null
ProductUiModel(
id = product.id,
title = product.name,
priceLabel = product.priceLabel,
isFavorite = isFavorite
)
}
Wenn products und favorites groß sind, suchst du für jedes Produkt erneut in der Favoritenliste. Besser ist, vorher ein Set aus IDs zu bauen:
val favoriteIds = favorites.map { it.id }.toSet()
val uiModels = products.map { product ->
ProductUiModel(
id = product.id,
title = product.name,
priceLabel = product.priceLabel,
isFavorite = product.id in favoriteIds
)
}
Das ist nicht nur performanter, sondern auch lesbarer. Der Code sagt: Favoriten sind eine Menge von IDs. Genau diese Klarheit hilft in Code-Reviews.
Achte außerdem auf Veränderbarkeit. MutableList, MutableMap und MutableSet sind nützlich, wenn du Daten gezielt aufbaust. Für öffentliche State-Objekte, Rückgabewerte aus Repositories oder UI-Modelle sind unveränderliche Typen oft besser. Sie verhindern, dass andere Codeteile deine Sammlung nebenbei ändern. In Compose ist das besonders wichtig, weil UI-Zustand nachvollziehbar bleiben soll.
Du kannst dein Verständnis prüfen, indem du beim Debuggen nicht nur Werte anschaust, sondern auch die Struktur. Frage dich: Warum ist das eine Liste und kein Set? Wird diese Map nach jeder UI-Aktualisierung neu aufgebaut? Gibt es doppelte IDs? In Tests kannst du genau solche Regeln absichern: Eine Funktion soll keine Duplikate liefern, eine Auswahl soll dieselbe ID nicht mehrfach speichern, ein Lookup soll bei unbekannter ID sauber null ergeben.
Fazit
Datenstrukturen sind kein trockenes Grundlagenwissen, sondern tägliches Handwerkszeug in Android-Projekten. Arrays, Lists, Maps und Sets geben deinen Daten Form und machen Absichten sichtbar: Reihenfolge, schneller Zugriff, Eindeutigkeit oder feste Größe. Prüfe bei deinem nächsten Feature bewusst, welche Operation am häufigsten ist, und lies deinen Code im Review mit dieser Frage: Passt die Struktur zur fachlichen Bedeutung und zum erwarteten App-Verhalten? Teste kleine Umwandlungen wie List zu Set oder associateBy zu Map, beobachte sie im Debugger und miss bei größeren Datenmengen, ob du unnötige Suche oder unnötige Kopien vermeidest.