Das Collections Framework
Das Sammlungsrahmen von Java ist eine einheitliche Architektur Gruppen von Objekten darzustellen und zu manipulieren. Bietet Schnittstellen, Implementierungen und Algorithmen zur effizienten Datenverwaltung.
Was du lernen wirst
- Hierarchie der Sammlungsschnittstelle
- List: ArrayList e LinkedList
- Set: HashSet, TreeSet e LinkedHashSet
- Map: HashMap, TreeMap e LinkedHashMap
- Queue e Deque
- Sammlungsalgorithmen und Dienstprogramme
Hierarchie der Collections
Hauptinterfaces
| Interface | Beschreibung | Implementierungen |
|---|---|---|
| Collection | Wurzel-Interface | - |
| List | Geordnete Sequenz mit Duplikaten | ArrayList, LinkedList |
| Set | Eindeutige Elemente | HashSet, TreeSet |
| Queue | FIFO-Warteschlange | LinkedList, PriorityQueue |
| Map | Schluessel-Wert-Paare | HashMap, TreeMap |
Iterable
|
Collection
/ | \
List Set Queue
| | |
ArrayList HashSet LinkedList
LinkedList TreeSet PriorityQueue
Map (separata)
|
HashMap TreeMap
List - Liste Ordinate
List Hält die Elemente in der Einfügungsreihenfolge
und erlaubt Duplikate. Es ist indiziert (Zugriff nach Standort).
ArrayList
import java.util.ArrayList;
import java.util.List;
public class RegistroStudenti {
public static void main(String[] args) {
// Creazione lista
List<String> studenti = new ArrayList<>();
// Aggiunta elementi
studenti.add("Mario Rossi");
studenti.add("Laura Bianchi");
studenti.add("Giuseppe Verdi");
studenti.add(1, "Anna Neri"); // Inserisce in posizione 1
// Accesso per indice
String primo = studenti.get(0); // "Mario Rossi"
String secondo = studenti.get(1); // "Anna Neri"
// Dimensione
int numStudenti = studenti.size(); // 4
// Modifica elemento
studenti.set(0, "Marco Rossi");
// Ricerca
boolean presente = studenti.contains("Laura Bianchi"); // true
int posizione = studenti.indexOf("Giuseppe Verdi"); // 3
// Rimozione
studenti.remove("Anna Neri"); // per valore
studenti.remove(0); // per indice
// Iterazione
for (String studente : studenti) {
System.out.println(studente);
}
// Iterazione con indice
for (int i = 0; i < studenti.size(); i++) {
System.out.println((i + 1) + ". " + studenti.get(i));
}
}
}
LinkedList
import java.util.LinkedList;
import java.util.List;
public class CodaEsami {
public static void main(String[] args) {
LinkedList<String> coda = new LinkedList<>();
// Operazioni specifiche LinkedList
coda.addFirst("Studente urgente"); // In testa
coda.addLast("Studente normale"); // In coda
coda.add("Altro studente"); // In coda
// Accesso primi/ultimi
String primo = coda.getFirst();
String ultimo = coda.getLast();
String primoPeek = coda.peekFirst(); // Non rimuove
// Rimozione primi/ultimi
String rimossoPrimo = coda.removeFirst();
String rimossoUltimo = coda.removeLast();
// Uso come Stack (LIFO)
coda.push("Nuovo in cima");
String estratto = coda.pop();
// Uso come Queue (FIFO)
coda.offer("Nuovo in coda");
String servito = coda.poll();
}
}
ArrayList vs. LinkedList
| Operation | ArrayList | LinkedList |
|---|---|---|
| get(index) | O(1) - Schnell | O(n) - Langsam |
| add(element) | O(1) amortisiert | O(1) |
| add(0, element) | O(n) - Langsam | O(1) - Schnell |
| remove(0) | O(n) - Langsam | O(1) - Schnell |
| Memoria | Weniger Overhead | Mehr Overhead (Knoten) |
Empfehlung: Verwenden Sie ArrayList als Standard, LinkedList nur für häufiges Einfügen/Entfernen von Overhead.
Set - Insiemi Unici
Set erlaubt keine Duplikate. Es ist ideal, wenn es benötigt wird
garantieren die Einzigartigkeit der Elemente.
HashSet
import java.util.HashSet;
import java.util.Set;
public class CorsiUnici {
public static void main(String[] args) {
Set<String> corsiFrequentati = new HashSet<>();
// Aggiunta elementi
corsiFrequentati.add("Programmazione");
corsiFrequentati.add("Database");
corsiFrequentati.add("Reti");
corsiFrequentati.add("Programmazione"); // Duplicato ignorato!
System.out.println(corsiFrequentati.size()); // 3
// Verifica presenza
if (corsiFrequentati.contains("Database")) {
System.out.println("Corso già frequentato");
}
// Rimozione
corsiFrequentati.remove("Reti");
// Iterazione (ordine NON garantito)
for (String corso : corsiFrequentati) {
System.out.println(corso);
}
// Operazioni insiemistiche
Set<String> nuoviCorsi = new HashSet<>();
nuoviCorsi.add("AI");
nuoviCorsi.add("Database");
// Unione
Set<String> tutti = new HashSet<>(corsiFrequentati);
tutti.addAll(nuoviCorsi);
// Intersezione
Set<String> comuni = new HashSet<>(corsiFrequentati);
comuni.retainAll(nuoviCorsi);
// Differenza
Set<String> soloVecchi = new HashSet<>(corsiFrequentati);
soloVecchi.removeAll(nuoviCorsi);
}
}
TreeSet e LinkedHashSet
import java.util.TreeSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class TipiSet {
public static void main(String[] args) {
// TreeSet: ordinato automaticamente
Set<Integer> voti = new TreeSet<>();
voti.add(28);
voti.add(30);
voti.add(24);
voti.add(27);
System.out.println(voti); // [24, 27, 28, 30] - ordinato!
// Metodi specifici TreeSet
TreeSet<Integer> votiTree = new TreeSet<>(voti);
Integer minimo = votiTree.first(); // 24
Integer massimo = votiTree.last(); // 30
Integer inferiore = votiTree.lower(28); // 27 (strettamente <)
Integer superiore = votiTree.higher(28); // 30 (strettamente >)
// Subset
Set<Integer> medi = votiTree.subSet(25, 29); // [27, 28]
// LinkedHashSet: mantiene ordine di inserimento
Set<String> ordineIscrizione = new LinkedHashSet<>();
ordineIscrizione.add("Mario");
ordineIscrizione.add("Anna");
ordineIscrizione.add("Luigi");
// Iterazione nell'ordine di inserimento
for (String nome : ordineIscrizione) {
System.out.println(nome); // Mario, Anna, Luigi
}
}
}
Vergleich Set
| Typ | Reihenfolge | Performance | Typische Verwendung |
|---|---|---|---|
| HashSet | Keiner | O(1) | Maximale Geschwindigkeit |
| TreeSet | Natuerlich/Comparator | O(log n) | Sortierte Elemente |
| LinkedHashSet | Einfuegereihenfolge | O(1) | Reihenfolge beibehalten |
Map - Coppie Chiave-Valore
Map ordnet eindeutige Schlüssel Werten zu. Es ist die Struktur
Daten, die am häufigsten für schnelle Suchvorgänge und Wörterbücher verwendet werden.
HashMap
import java.util.HashMap;
import java.util.Map;
public class RegistroVoti {
public static void main(String[] args) {
// Mappa studente -> voto
Map<String, Integer> voti = new HashMap<>();
// Inserimento
voti.put("Mario Rossi", 28);
voti.put("Laura Bianchi", 30);
voti.put("Giuseppe Verdi", 25);
// Accesso
Integer votoMario = voti.get("Mario Rossi"); // 28
Integer votoAssente = voti.get("Inesistente"); // null
// getOrDefault: evita null
Integer votoSicuro = voti.getOrDefault("Inesistente", 0);
// Verifica presenza
if (voti.containsKey("Laura Bianchi")) {
System.out.println("Laura è registrata");
}
if (voti.containsValue(30)) {
System.out.println("Qualcuno ha preso 30!");
}
// Aggiornamento
voti.put("Mario Rossi", 29); // Sovrascrive
// putIfAbsent: inserisce solo se assente
voti.putIfAbsent("Mario Rossi", 18); // Non cambia (già presente)
// Rimozione
voti.remove("Giuseppe Verdi");
voti.remove("Laura Bianchi", 30); // Rimuove solo se valore corrisponde
// Dimensione
int numStudenti = voti.size();
boolean vuota = voti.isEmpty();
}
}
Map<String, Integer> voti = new HashMap<>();
voti.put("Mario", 28);
voti.put("Laura", 30);
voti.put("Giuseppe", 25);
// Iterazione su chiavi
for (String studente : voti.keySet()) {
System.out.println(studente);
}
// Iterazione su valori
for (Integer voto : voti.values()) {
System.out.println(voto);
}
// Iterazione su coppie (Entry) - più efficiente
for (Map.Entry<String, Integer> entry : voti.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// forEach con lambda (Java 8+)
voti.forEach((studente, voto) ->
System.out.println(studente + " ha preso " + voto)
);
TreeMap e LinkedHashMap
import java.util.TreeMap;
import java.util.LinkedHashMap;
public class TipiMap {
public static void main(String[] args) {
// TreeMap: chiavi ordinate
TreeMap<String, Integer> alfabetico = new TreeMap<>();
alfabetico.put("Zeta", 1);
alfabetico.put("Alfa", 2);
alfabetico.put("Beta", 3);
// Iterazione in ordine alfabetico
for (String key : alfabetico.keySet()) {
System.out.println(key); // Alfa, Beta, Zeta
}
// Metodi navigazione
String prima = alfabetico.firstKey(); // "Alfa"
String ultima = alfabetico.lastKey(); // "Zeta"
String inferiore = alfabetico.lowerKey("Beta"); // "Alfa"
// LinkedHashMap: ordine di inserimento
LinkedHashMap<String, Integer> ordinato = new LinkedHashMap<>();
ordinato.put("Primo", 1);
ordinato.put("Secondo", 2);
ordinato.put("Terzo", 3);
// Mantiene ordine di inserimento
for (String key : ordinato.keySet()) {
System.out.println(key); // Primo, Secondo, Terzo
}
}
}
Queue e Deque
Queue stellt eine FIFO-Warteschlange (First-In-First-Out) dar.
Deque es ist ein Schwanz mit zwei Enden.
import java.util.Queue;
import java.util.LinkedList;
import java.util.PriorityQueue;
public class GestioneCode {
public static void main(String[] args) {
// Coda FIFO con LinkedList
Queue<String> codaSportello = new LinkedList<>();
// offer: aggiunge in coda
codaSportello.offer("Cliente 1");
codaSportello.offer("Cliente 2");
codaSportello.offer("Cliente 3");
// peek: guarda primo senza rimuovere
String prossimo = codaSportello.peek(); // "Cliente 1"
// poll: rimuove e restituisce primo
String servito = codaSportello.poll(); // "Cliente 1"
System.out.println("Servito: " + servito);
// PriorityQueue: ordine per priorità
Queue<Integer> priorità = new PriorityQueue<>();
priorità.offer(30);
priorità.offer(10);
priorità.offer(20);
// Estrae in ordine di priorità (naturale = crescente)
while (!priorità.isEmpty()) {
System.out.println(priorità.poll()); // 10, 20, 30
}
// PriorityQueue con ordine personalizzato
Queue<String> perLunghezza = new PriorityQueue<>(
(a, b) -> a.length() - b.length()
);
perLunghezza.offer("lungo");
perLunghezza.offer("xx");
perLunghezza.offer("medio");
while (!perLunghezza.isEmpty()) {
System.out.println(perLunghezza.poll()); // xx, lungo, medio
}
}
}
import java.util.Deque;
import java.util.ArrayDeque;
public class CodaDoppia {
public static void main(String[] args) {
Deque<String> deque = new ArrayDeque<>();
// Operazioni in testa
deque.addFirst("A");
deque.offerFirst("B");
// Operazioni in coda
deque.addLast("C");
deque.offerLast("D");
// Stato: [B, A, C, D]
// Rimozione da testa
String primo = deque.removeFirst(); // "B"
String primoPoll = deque.pollFirst(); // "A"
// Rimozione da coda
String ultimo = deque.removeLast(); // "D"
String ultimoPoll = deque.pollLast(); // "C"
// Uso come Stack (LIFO)
Deque<String> stack = new ArrayDeque<>();
stack.push("Primo"); // In cima
stack.push("Secondo");
stack.push("Terzo");
while (!stack.isEmpty()) {
System.out.println(stack.pop()); // Terzo, Secondo, Primo
}
}
}
Algorithmen der Collections-Klasse
Die Klasse Collections stellt statische Methoden bereit
für allgemeine Operationen an Sammlungen.
import java.util.*;
public class UtilityCollections {
public static void main(String[] args) {
List<Integer> voti = new ArrayList<>(
Arrays.asList(25, 30, 28, 24, 27, 30)
);
// Ordinamento
Collections.sort(voti);
System.out.println(voti); // [24, 25, 27, 28, 30, 30]
// Ordinamento inverso
Collections.sort(voti, Collections.reverseOrder());
System.out.println(voti); // [30, 30, 28, 27, 25, 24]
// Ricerca binaria (lista deve essere ordinata!)
Collections.sort(voti);
int indice = Collections.binarySearch(voti, 27);
// Min e Max
int minimo = Collections.min(voti); // 24
int massimo = Collections.max(voti); // 30
// Frequenza
int cont30 = Collections.frequency(voti, 30); // 2
// Mescolare (shuffle)
Collections.shuffle(voti);
// Invertire
Collections.reverse(voti);
// Riempire
Collections.fill(voti, 0); // Tutti a 0
// Scambiare elementi
Collections.swap(voti, 0, 1);
// Rotazione
List<String> lista = new ArrayList<>(
Arrays.asList("A", "B", "C", "D")
);
Collections.rotate(lista, 1); // [D, A, B, C]
Collections.rotate(lista, -1); // [A, B, C, D]
// Liste immutabili
List<String> immutabile = Collections.unmodifiableList(lista);
// immutabile.add("X"); // UnsupportedOperationException!
// Lista vuota immutabile
List<String> vuota = Collections.emptyList();
// Singleton (lista con un elemento)
List<String> singolo = Collections.singletonList("Unico");
}
}
Vollstaendiges Beispiel: Kursverwaltungssystem
package scuola;
import java.util.*;
public class Corso {
private String nome;
private String docente;
private int maxStudenti;
private Set<String> studentiIscritti;
private Map<String, List<Integer>> votiStudenti;
public Corso(String nome, String docente, int maxStudenti) {
this.nome = nome;
this.docente = docente;
this.maxStudenti = maxStudenti;
this.studentiIscritti = new LinkedHashSet<>();
this.votiStudenti = new HashMap<>();
}
public boolean iscrivi(String studente) {
if (studentiIscritti.size() >= maxStudenti) {
System.out.println("Corso pieno!");
return false;
}
if (studentiIscritti.contains(studente)) {
System.out.println(studente + " già iscritto!");
return false;
}
studentiIscritti.add(studente);
votiStudenti.put(studente, new ArrayList<>());
return true;
}
public void registraVoto(String studente, int voto) {
if (!studentiIscritti.contains(studente)) {
System.out.println(studente + " non iscritto!");
return;
}
if (voto < 18 || voto > 30) {
System.out.println("Voto non valido");
return;
}
votiStudenti.get(studente).add(voto);
}
public double calcolaMedia(String studente) {
List<Integer> voti = votiStudenti.get(studente);
if (voti == null || voti.isEmpty()) {
return 0;
}
return voti.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0);
}
public Map<String, Double> classificaStudenti() {
Map<String, Double> classifica = new TreeMap<>();
for (String studente : studentiIscritti) {
classifica.put(studente, calcolaMedia(studente));
}
return classifica;
}
public List<String> getStudentiOrdinatiPerMedia() {
List<String> ordinati = new ArrayList<>(studentiIscritti);
ordinati.sort((s1, s2) ->
Double.compare(calcolaMedia(s2), calcolaMedia(s1))
);
return ordinati;
}
public void stampaRiepilogo() {
System.out.println("╔══════════════════════════════════════╗");
System.out.println("║ Corso: " + nome);
System.out.println("║ Docente: " + docente);
System.out.println("║ Iscritti: " + studentiIscritti.size() + "/" + maxStudenti);
System.out.println("╠══════════════════════════════════════╣");
for (String studente : getStudentiOrdinatiPerMedia()) {
List<Integer> voti = votiStudenti.get(studente);
double media = calcolaMedia(studente);
System.out.printf("║ %-20s Media: %.2f%n", studente, media);
System.out.println("║ Voti: " + voti);
}
System.out.println("╚══════════════════════════════════════╝");
}
// Getters
public String getNome() { return nome; }
public Set<String> getStudentiIscritti() {
return Collections.unmodifiableSet(studentiIscritti);
}
}
package scuola;
import java.util.*;
public class GestioneCorsi {
private Map<String, Corso> corsi;
private Map<String, Set<String>> iscrizioniStudente;
public GestioneCorsi() {
this.corsi = new HashMap<>();
this.iscrizioniStudente = new HashMap<>();
}
public void aggiungiCorso(Corso corso) {
corsi.put(corso.getNome(), corso);
}
public boolean iscriviStudente(String studente, String nomeCorso) {
Corso corso = corsi.get(nomeCorso);
if (corso == null) {
System.out.println("Corso non trovato: " + nomeCorso);
return false;
}
if (corso.iscrivi(studente)) {
iscrizioniStudente
.computeIfAbsent(studente, k -> new HashSet<>())
.add(nomeCorso);
return true;
}
return false;
}
public Set<String> getCorsiStudente(String studente) {
return iscrizioniStudente.getOrDefault(studente, Collections.emptySet());
}
public void stampaStatistiche() {
System.out.println("\n=== STATISTICHE GLOBALI ===");
System.out.println("Totale corsi: " + corsi.size());
int totaleIscrizioni = iscrizioniStudente.values()
.stream()
.mapToInt(Set::size)
.sum();
System.out.println("Totale iscrizioni: " + totaleIscrizioni);
// Studente con più corsi
String piuAttivo = iscrizioniStudente.entrySet()
.stream()
.max(Comparator.comparingInt(e -> e.getValue().size()))
.map(Map.Entry::getKey)
.orElse("Nessuno");
System.out.println("Studente più attivo: " + piuAttivo);
}
public static void main(String[] args) {
GestioneCorsi gestione = new GestioneCorsi();
// Crea corsi
gestione.aggiungiCorso(new Corso("Programmazione", "Prof. Bianchi", 30));
gestione.aggiungiCorso(new Corso("Database", "Prof. Rossi", 25));
gestione.aggiungiCorso(new Corso("Reti", "Prof. Verdi", 20));
// Iscrivi studenti
gestione.iscriviStudente("Mario", "Programmazione");
gestione.iscriviStudente("Mario", "Database");
gestione.iscriviStudente("Laura", "Programmazione");
gestione.iscriviStudente("Laura", "Reti");
gestione.iscriviStudente("Giuseppe", "Database");
// Registra voti
Corso prog = gestione.corsi.get("Programmazione");
prog.registraVoto("Mario", 28);
prog.registraVoto("Mario", 30);
prog.registraVoto("Laura", 27);
Corso db = gestione.corsi.get("Database");
db.registraVoto("Mario", 25);
db.registraVoto("Giuseppe", 30);
// Stampa riepilogo
prog.stampaRiepilogo();
db.stampaRiepilogo();
// Statistiche
gestione.stampaStatistiche();
System.out.println("\nCorsi di Mario: " + gestione.getCorsiStudente("Mario"));
}
}
Fazit
Das Collections Framework ist für jeden Java-Entwickler unverzichtbar. Die Wahl der richtigen Kollektion hängt von den Anforderungen ab:
Wichtige Punkte zum Merken
- ArrayList: Schneller Indexzugriff, Standard fuer Listen
- LinkedList: Schnelle Einfuegungen/Entfernungen am Anfang
- HashSet: Einzigartigkeit mit maximaler Geschwindigkeit
- TreeSet: Einzigartigkeit bei der Bestellung
- HashMap: Schnelle Schluessel-Wert-Suche
- TreeMap: Sortierte Schluessel
- Prioritätswarteschlange: Extraktion nach Priorität
- ArrayDeque: Effizienter Stack/Queue
Im nächste Artikel werden wir uns mit la befassen Management von Fehlern und Ausnahmen: Try-Catch, Throw, Ausnahmen aktiviert und deaktiviert.







