Array
Ein Array ist eine grundlegende Datenstruktur in der Informatik, die eine geordnete Sammlung von Elementen gleichen Typs speichert. Die Elemente werden dabei direkt nacheinander im Arbeitsspeicher abgelegt, was einen sehr schnellen Zugriff auf jedes beliebige Element ermöglicht. Du kannst dir ein Array wie eine Schubladenbox vorstellen, bei der jede Schublade ein Element enthält und über ihre Position - den sogenannten Index - eindeutig identifizierbar ist.
Arrays gehören zu den am häufigsten verwendeten Datenstrukturen und bilden die Grundlage für viele Algorithmen und Programme. Sie sind besonders wichtig in leistungskritischen Bereichen wie der Grafikprogrammierung, Physiksimulationen und dem maschinellen Lernen.
C
Ein Array besteht aus einer festgelegten Anzahl von Speicherplätzen, die alle den gleichen Datentyp aufnehmen können. Diese Einschränkung auf einen Datentyp ist kein Zufall, sondern ermöglicht eine elegante Berechnung der Speicheradresse: Da alle Elemente gleich viel Speicherplatz benötigen, kann die Position des n-ten Elements durch eine einfache Formel berechnet werden.
Speicheradresse = Basisadresse + (Index × Elementgröße)
Beispiel mit einem Integer-Array (4 Bytes pro Element):
- Basisadresse: 1000
- Element 0: 1000 + (0 × 4) = 1000
- Element 1: 1000 + (1 × 4) = 1004
- Element 2: 1000 + (2 × 4) = 1008
Indexierung: Warum bei 0 beginnen?
Die meisten Programmiersprachen wie Java, C, Python und JavaScript verwenden eine 0-basierte Indexierung. Das bedeutet, dass das erste Element den Index 0 hat, das zweite den Index 1, und so weiter. Für ein Array mit n Elementen gehen die gültigen Indizes von 0 bis n-1.
Diese Konvention hat technische Gründe: Der Index beschreibt einen Offset (Versatz) vom Anfang des Arrays. Ein Index von 0 bedeutet keinen Versatz - also das erste Element. Dadurch funktioniert die Adressberechnung ohne zusätzliche Subtraktion, was in frühen Programmiersprachen Rechenzeit sparte und mathematisch eleganter ist.
| Sprache | Indexierung | Beispiel |
|---|---|---|
| Java, C, C++, Python, JavaScript | 0-basiert | array[0] ist das erste Element |
| Fortran, R, Julia, MATLAB | 1-basiert | array(1) ist das erste Element |
Statische vs. dynamische Arrays
Arrays werden grundsätzlich in zwei Kategorien eingeteilt, die sich in ihrer Flexibilität unterscheiden:
Statische Arrays
Ein statisches Array hat eine festgelegte Größe, die bei der Erstellung angegeben wird und danach nicht mehr verändert werden kann. Du musst also vorher wissen, wie viele Elemente du speichern möchtest.
// Java: Statisches Array mit 5 Elementen
int[] zahlen = new int[5];
zahlen[0] = 10;
zahlen[1] = 20;
// Die Größe bleibt bei 5, auch wenn nicht alle Plätze genutzt werden
Dynamische Arrays
Ein dynamisches Array kann seine Größe zur Laufzeit anpassen. Intern verwendet es ein statisches Array und verdoppelt dessen Größe, wenn es voll wird. Beispiele sind ArrayList in Java, Listen in Python und vector in C++.
// Java: ArrayList als dynamisches Array
import java.util.ArrayList;
ArrayList<Integer> zahlen = new ArrayList<>();
zahlen.add(10); // Größe passt sich automatisch an
zahlen.add(20);
zahlen.add(30); // Kein Limit vorgegeben
Mehrdimensionale Arrays
Neben eindimensionalen Arrays gibt es auch mehrdimensionale Arrays - Arrays von Arrays. Das am häufigsten verwendete ist das zweidimensionale Array, das man sich als Tabelle mit Zeilen und Spalten vorstellen kann.
// Java: 2D-Array (3 Zeilen, 4 Spalten)
int[][] matrix = new int[3][4];
// Zugriff über zwei Indizes: [Zeile][Spalte]
matrix[0][0] = 1; // Erste Zeile, erste Spalte
matrix[1][2] = 5; // Zweite Zeile, dritte Spalte
matrix[2][3] = 9; // Dritte Zeile, vierte Spalte
Zweidimensionale Arrays werden häufig in der Bildverarbeitung eingesetzt, wo jedes Pixel durch seine x- und y-Koordinaten adressiert wird. Auch Spielfelder, Tabellen und mathematische Matrizen lassen sich damit abbilden.
Zeitkomplexität von Array-Operationen
Die Effizienz von Operationen auf Arrays wird mit der Big-O-Notation beschrieben. Arrays glänzen besonders beim direkten Zugriff, haben aber Schwächen beim Einfügen und Löschen:
| Operation | Zeitkomplexität | Erklärung |
|---|---|---|
| Zugriff auf Element | O(1) | Konstant - unabhängig von der Array-Größe |
| Suchen (unsortiert) | O(n) | Linear - im Schnitt muss die Hälfte durchsucht werden |
| Suchen (sortiert, binär) | O(log n) | Logarithmisch - Array wird wiederholt halbiert |
| Einfügen am Ende | O(1)* | Konstant (amortisiert bei dynamischen Arrays) |
| Einfügen in der Mitte | O(n) | Linear - alle nachfolgenden Elemente müssen verschoben werden |
| Löschen | O(n) | Linear - nachfolgende Elemente müssen aufgerückt werden |
| Durchlaufen (Iteration) | O(n) | Linear - jedes Element wird einmal besucht |
Der O(1)-Zugriff ist die große Stärke von Arrays: Egal ob du auf das erste oder millionste Element zugreifst, die Zeit ist immer gleich. Dies liegt an der direkten Adressberechnung durch den Index.
Arrays in verschiedenen Programmiersprachen
Java
// Statisches Array deklarieren und initialisieren
int[] zahlen = {10, 20, 30, 40, 50};
// Zugriff und Iteration
for (int i = 0; i < zahlen.length; i++) {
System.out.println(zahlen[i]);
}
// Oder mit for-each
for (int zahl : zahlen) {
System.out.println(zahl);
}
Python
# Python-Listen sind dynamische Arrays
zahlen = [10, 20, 30, 40, 50]
# Element hinzufügen
zahlen.append(60)
# Zugriff über Index
print(zahlen[0]) # Ausgabe: 10
# Negative Indizes (vom Ende aus)
print(zahlen[-1]) # Ausgabe: 60 (letztes Element)
# Slicing (Teilbereich)
print(zahlen[1:3]) # Ausgabe: [20, 30]
JavaScript
// Array erstellen
const zahlen = [10, 20, 30, 40, 50];
// Element hinzufügen
zahlen.push(60);
// Array-Methoden
const verdoppelt = zahlen.map(x => x * 2);
const nurGrosse = zahlen.filter(x => x > 25);
const summe = zahlen.reduce((a, b) => a + b, 0);
#include <stdio.h>
int main() {
// Statisches Array
int zahlen[5] = {10, 20, 30, 40, 50};
// Zugriff
printf("%d\n", zahlen[0]); // Ausgabe: 10
// Größe berechnen
int groesse = sizeof(zahlen) / sizeof(zahlen[0]);
return 0;
}
Arrays vs. andere Datenstrukturen
Die Wahl zwischen Arrays und anderen Datenstrukturen hängt von den Anforderungen deiner Anwendung ab:
| Kriterium | Array | Verkettete Liste |
|---|---|---|
| Zugriff auf Element | O(1) - sehr schnell | O(n) - muss durchlaufen werden |
| Einfügen/Löschen | O(n) - Verschieben nötig | O(1) - nur Zeiger anpassen |
| Speicherverbrauch | Geringer (nur Daten) | Höher (zusätzliche Zeiger) |
| Cache-Effizienz | Sehr gut (zusammenhängend) | Schlecht (verstreut im Speicher) |
| Größenanpassung | Aufwendig (Kopieren) | Einfach (neuer Knoten) |
Verwende Arrays, wenn:
- Die Größe bekannt oder relativ stabil ist
- Häufiger Zugriff auf zufällige Positionen erfolgt
- Performance und Speichereffizienz wichtig sind
Verwende verkettete Listen, wenn:
- Häufiges Einfügen/Löschen in der Mitte nötig ist
- Die Größe stark variiert
- Sequentielles Durchlaufen ausreicht
Häufige Fehler vermeiden
Der häufigste Fehler bei Arrays ist der ArrayIndexOutOfBoundsException (Java) oder Index out of bounds (andere Sprachen). Dieser tritt auf, wenn du versuchst, auf einen Index zuzugreifen, der außerhalb des gültigen Bereichs liegt.
int[] zahlen = new int[5]; // Gültige Indizes: 0, 1, 2, 3, 4
// Fehler! Index 5 existiert nicht
zahlen[5] = 100; // ArrayIndexOutOfBoundsException
// Richtig: Index prüfen
int index = 5;
if (index >= 0 && index < zahlen.length) {
zahlen[index] = 100;
}
Typische Anwendungsfälle
- Bildverarbeitung: Pixel als 2D-Array von Farbwerten
- Spieleentwicklung: Spielfelder, Inventare, Highscore-Listen
- Datenbanken: Ergebnismengen als Arrays von Datensätzen
- Wissenschaftliches Rechnen: Vektoren und Matrizen für Berechnungen
- Webentwicklung: JSON-Arrays für API-Antworten
- Maschinelles Lernen: Tensoren als mehrdimensionale Arrays