Algorithmen und Datenstrukturen in Java: Von der Theorie zur Praxis
Das Verständnis von Algorithmen und Datenstrukturen ist die Grundlage, auf der die Karriere eines jeden Java-Entwicklers aufbaut. Ohne dieses Wissen ist es schwierig, effizienten, skalierbaren und wartbaren Code zu schreiben. In diesem Artikel werden wir die wichtigsten Datenstrukturen und Algorithmen behandeln, die Sie täglich verwenden werden, und ihre Implementierung in Java mit praktischen Beispielen zeigen.
1. Grundlegende Datenstrukturen im Java Collections Framework
Java bietet ein leistungsstarkes Collections-Framework, das die wichtigsten Datenstrukturen umfasst. Betrachten wir die drei beliebtesten.
ArrayList – Dynamisches Array
ArrayList ist eine Implementierung eines dynamischen Arrays. Es erweitert sich automatisch beim Hinzufügen neuer Elemente, was es zu einer ausgezeichneten Wahl für Listen mit unbekannter Größe macht.
import java.util.ArrayList;
public class ArrayListExample { public static void main(String[] args) { ArrayList<String> fruits = new ArrayList<>(); fruits.add("Apfel"); fruits.add("Banane"); fruits.add("Orange");
System.out.println("Zweites Element: " + fruits.get(1)); // Banane fruits.remove(0); System.out.println("Größe nach dem Löschen: " + fruits.size()); // 2 for (String fruit : fruits) { System.out.println(fruit); } }}Komplexität der Operationen: Zugriff über Index – O(1), Einfügen/Löschen in der Mitte – O(n).
HashMap – Hash-Tabelle
HashMap speichert Schlüssel-Wert-Paare und ermöglicht einen nahezu sofortigen Zugriff auf Daten über den Schlüssel.
import java.util.HashMap;
public class HashMapExample { public static void main(String[] args) { HashMap<String, Integer> ages = new HashMap<>(); ages.put("Anna", 25); ages.put("Ivan", 30); ages.put("Maria", 28);
System.out.println("Alter von Anna: " + ages.get("Anna")); // 25 // Prüfen, ob ein Schlüssel existiert if (ages.containsKey("Ivan")) { System.out.println("Ivan gefunden!"); } // Alle Einträge durchlaufen for (String name : ages.keySet()) { System.out.println(name + ": " + ages.get(name) + " Jahre"); } }}Komplexität der Operationen: Einfügen, Suchen, Löschen – im Durchschnitt O(1).
LinkedList – Doppelt verkettete Liste
LinkedList besteht aus Knoten, die jeweils Referenzen auf das vorherige und das nächste Element speichern. Ideal für häufige Einfügungen und Löschungen am Anfang oder Ende der Liste.
import java.util.LinkedList;
public class LinkedListExample { public static void main(String[] args) { LinkedList<Integer> queue = new LinkedList<>(); // Als Warteschlange verwenden (FIFO) queue.addLast(1); queue.addLast(2); queue.addLast(3); System.out.println("Erster in der Warteschlange: " + queue.getFirst()); // 1 queue.removeFirst(); System.out.println("Nach dem Löschen: " + queue); // [2, 3] // Als Stapel verwenden (LIFO) queue.push(10); // am Anfang hinzufügen queue.push(20); System.out.println("Stapelspitze: " + queue.pop()); // 20 }}Komplexität der Operationen: Einfügen/Löschen am Anfang/Ende – O(1), Zugriff über Index – O(n).
2. Sortieralgorithmen in Java
Sortieren ist eine der häufigsten Aufgaben. Betrachten wir zwei klassische Implementierungen.
Bubble Sort
Ein einfacher, aber langsamer Algorithmus. Wird zu Lehrzwecken verwendet.
public class BubbleSort { public static void bubbleSort(int[] arr) { int n = arr.length; boolean swapped; for (i