Vertiefung der Programmierung

Datenstrukturen 

Motivation

Welche Datenstrukturen
kennen wir
schon?

Java Collections

Wichtige Datenstrukturen

List

Map

Set

Diese Datenstrukturen (Collections) sind Schnittstellen, deren Methoden durch konkrete Implementierungen umgesetzt werden müssen, z.B. ArrayList<String>.

List

[E] bezieht sich auf den sog. generischen Typ, d.h. hier können sämtliche Klassen angegeben werden

Exkurs: Hash-Tabellen

Eingabe: "Marian"

{ Hash-Funktion }

Ausgabe: Bucket

BucketWert
0Hans
......
13Marian

Hash-Tabellen können nahezu 

genauso schnell auf Elemente zugreifen wie Arrays ➔ O(1) 
(reale Laufzeit abhängig von Hash-Funktion)

Set (Mengen)

[E] bezieht sich auf den sog. generischen Typ, d.h. hier können sämtliche Klassen angegeben werden

📦 import java.util.Set;
📦 import java.util.HashSet;

Set (Mengen)

  • Sets implementieren mathematisch gesehen Mengen
  • Diese Datenstruktur sorgt automatisch dafür, dass keine Duplikate angelegt werden
    • Bsp.: {1, 2, 2, 3, 3, 4} → {1, 2, 3, 4}
  • ​Der Zugriff ist sehr schnell
    • ​HashSet: O(1)
  • ​TreeSet sortiert automatisch

Sets sind sehr gut geeignet, wenn man sicherstellen möchte, dass keine Duplikate existieren dürfen.  

Set (Wichtige Methoden)

  • boolean add(E element)

    • Fügt ein Element hinzu, wenn es noch nicht im Set vorhanden ist

    • Beispiel: set.add("Max");

  • boolean remove(Object o)

    • Entfernt ein bestimmtes Element aus dem Set

    • Beispiel: set.remove("Max");

  • boolean contains(Object o)

    • Prüft, ob ein bestimmtes Element im Set vorhanden ist.

    • Beispiel: set.contains("Max");

  • ​int size()

    • Liefert die Anzahl der gespeicherten Elemente

  • boolean isEmpty()

    • Prüft, ob das Set keine Elemente enthält.

Set (Beispiel)

import java.util.Set;
import java.util.HashSet;

public class Main {

    public static void main(String[] args) {
        Set<Integer> numbers = new HashSet<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(2);

        for(Integer elem : numbers) {
            System.out.println(elem);
        }

    }   
}

Ausgabe:
1
2

⌨️ Set (Beispiel)

Map

  • Map speichert Daten als sog. Schlüssel-Wert-Paare (Key-Value-Pairs)

  • Jeder Schlüssel (Key) ist eindeutig, ein Wert darf mehrfach vorkommen

  • Suche nach einem Wert über den Schlüssel ist sehr schnell
     O(1) für HashMap

  • Verändern der Map:

    • Einträge hinzufügen:  put(K key, V value)

    • Einträge lesen:   get(K key)

    • Einträge löschen:   remove(K key) 

Maps eignen sich sehr gut für als universelle "Nachschlagewerke". 

Map (Implementierungen)

[K] und [V] beziehen sich auf sog. generische Typen, d.h. hier können sämtliche Klassen angegeben werden.
[K] ist Key (=Schlüssel) und [V] ist Value (=Wert)

📦 import java.util.Map;
📦 import java.util.HashMap;

Map (Beispiel)

import java.util.Map;
import java.util.HashMap;

public class Main {
    public static void main(String[] args) {

        Map<String, Integer> str2num = new HashMap<>(); 

        // Schlüssel-Werte-Paare einfügen
        str2num.put("Eins", 1);
        str2num.put("Zweihundertdrei", 203);
        
        // Schlüssel-Werte-Paare ausgeben
        System.out.println(str2num.get("Eins"));
    }   
}

Map (Wichtige Methoden)

  • V put(K key, V value)
    • Fügt ein Schlüssel-Wert-Paar hinzu oder überschreibt den Wert, wenn der Schlüssel schon existiert.
    • Beispiel: map.put("Max", 20);
  • V get(Object key)
    • Gibt den Wert zum angegebenen Schlüssel zurück (oder null, wenn nicht vorhanden).
    • Beispiel: map.get("Max");
  • V remove(Object key)
    • Entfernt das Eintrag-Paar zum Schlüssel.
    • Beispiel: map.remove("Max");
  • boolean containsKey(Object key)
    • Prüft, ob ein bestimmter Schlüssel existiert.
    • Beispiel: map.containsKey("Max");

Map (Wichtige Methoden)

  • boolean containsValue(Object value)
    • Prüft, ob ein bestimmter Wert existiert
  • int size()
    • Gibt die Anzahl der Einträge zurück
  • boolean isEmpty()
    • Prüft, ob die Map leer ist
  • void clear()
    • Entfernt alle Einträge.
  • Set<K> keySet()
    • Gibt alle Schlüssel als Set zurück.
      • Beispiel: map.keySet();
  • Collection<V> values()
    • Gibt alle Werte zurück
      • Beispiel: map.values();

⌨️ Map (Beispiel)

Live
coding

Eigene Klassen als Elemente

Eigene Klassen als Elemente

  • Für Java-eigene Klasse (z.B. Double, String, Integer, ...) werden Hash-Codes korrekt bestimmt
    → Werte, auf die Objekte zeigen, werden genutzt
    z.B. String s = "VProg"; Double d = 2.;

     
  • Das funktioniert nicht für eigene Klassen.
    Warum nicht?
     → Beispiel
public class Person {
  private String name;
  private boolean isActive;
  
  public Person(String name, boolean isActive) {
    this.name = name;
    this.isActive = isActive;
  }
}

Welcher Wert soll hier als Schlüssel
dienen?

equal- und hashCode-Methoden

import java.util.Set;
import java.util.HashSet;

public class Main {

    // Here is the start!
    public static void main(String[] args) {
        Set<Person> set = new HashSet<>();

        set.add(new Person("Marian"));
        set.add(new Person("Marian"));

        for (Person elem : set) {
            System.out.println(elem.getName());
        }
    }   
}
import java.util.Objects;

class Person {
   String name;

   Person(String name) {
      this.name = name;
   }

   public String getName() {
      return this.name;
   }

   @Override
   public boolean equals(Object otherObject) {
      String other = ((Person) otherObject).getName();
      return this.name.equals(other);
   }

    @Override
    public int hashCode() {
        return Objects.hash(this.name);
    }
   
}

📦 import java.util.Objects;

Java Collections (Ausblick)

Zusammenfassung

  • Je nach Anwendungsfall sind unterschiedliche Datenstrukturen geeignet

    • Easy going: Listen (v.a. ArrayList)

    • Schneller Zugriff (bei vielen Elementen): Map 

    • Nachschlagen (z.B. "read"  "lesen"): Map

    • Duplikate vermeiden: Set

  • Bei eigenen Klassen müssen equals() und hashCode() überschrieben werden