Array e Collezioni in C#
Organizzare e gestire gruppi di dati correlati con strutture efficienti
🎯 Cosa Imparerai in Questa Lezione
Finora abbiamo lavorato con singole variabili, ma cosa succede quando devi gestire
centinaia di valori? Gli array e le collezioni
sono strutture che permettono di memorizzare e manipolare gruppi di dati correlati in modo efficiente.
Immagina di dover memorizzare i voti di 30 studenti. Senza array dovresti creare 30 variabili
(voto1, voto2, …, voto30). Con un array?
Una sola variabile: int[] voti = new int[30];
- Dimensione fissa
- Più performanti
- Sintassi semplice
- Tipo primitivo
- Dimensione dinamica
- Più funzionalità
- Metodi built-in
- Più flessibili
📦 Array
Un array è una struttura dati che contiene un numero fisso di
elementi dello stesso tipo, organizzati in sequenza e accessibili tramite un indice numerico
che parte da 0.
💡 Come Funziona un Array
Pensa a un array come a una fila di scatole numerate. Ogni scatola può contenere un valore
dello stesso tipo. La prima scatola ha indice 0, la seconda 1, e così via.
Array: [ 10 | 20 | 30 | 40 | 50 ] Indice: 0 1 2 3 4
Dichiarazione e Inizializzazione
Dichiara un array specificando il tipo seguito da [].
// Solo dichiarazione (array non inizializzato) int[] numeri; // Dichiarazione + inizializzazione con dimensione numeri = new int[5]; // Array di 5 interi (tutti inizializzati a 0) // In una sola riga string[] nomi = new string[3]; // Array di 3 stringhe (null)
Puoi inizializzare l’array direttamente con i valori.
// Sintassi completa
int[] voti = new int[] { 28, 30, 25, 27, 29 };
// Sintassi abbreviata (consigliata)
string[] nomi = { "Mario", "Luigi", "Peach" };
// Array di double
double[] temperature = { 18.5, 22.3, 19.8, 21.0 };
// Il compilatore conta automaticamente gli elementi!
Usa l’indice tra parentesi quadre per leggere o modificare elementi.
int[] voti = { 28, 30, 25, 27, 29 };
// Lettura (gli indici partono da 0!)
Console.WriteLine(voti[0]); // Output: 28 (primo elemento)
Console.WriteLine(voti[2]); // Output: 25 (terzo elemento)
// Modifica
voti[0] = 29; // Cambia il primo voto a 29
voti[4] = 30; // Cambia l'ultimo voto a 30
// Lunghezza dell'array
Console.WriteLine(voti.Length); // Output: 5
// Ultimo elemento (usando Length)
int ultimoVoto = voti[voti.Length - 1]; // voti[4]
Gli indici degli array partono da 0. In un array di 5 elementi,
gli indici validi sono da 0 a 4. Accedere a un indice fuori range causa un’eccezione!
int[] numeri = new int[5]; // Indici validi: 0, 1, 2, 3, 4 Console.WriteLine(numeri[0]); // ✅ OK Console.WriteLine(numeri[4]); // ✅ OK Console.WriteLine(numeri[5]); // ❌ ERRORE! IndexOutOfRangeException Console.WriteLine(numeri[-1]); // ❌ ERRORE! Indice negativo
Array Multidimensionali
C# supporta array con più dimensioni, utili per rappresentare dati tabulari,
matrici, griglie di gioco, ecc.
Rappresenta dati in righe e colonne, come una tabella.
// Dichiarazione: [righe, colonne]
int[,] matrice = new int[3, 2]
{
{ 1, 2 }, // Riga 0
{ 3, 4 }, // Riga 1
{ 5, 6 } // Riga 2
};
// Accesso agli elementi: [riga, colonna]
Console.WriteLine(matrice[0, 0]); // Output: 1
Console.WriteLine(matrice[0, 1]); // Output: 2
Console.WriteLine(matrice[2, 1]); // Output: 6
// Modifica
matrice[1, 0] = 10; // Cambia 3 → 10
// Dimensioni
int righe = matrice.GetLength(0); // 3 righe
int colonne = matrice.GetLength(1); // 2 colonne
// Visualizzazione matrice
Console.WriteLine("Matrice:");
/*
1 2
10 4
5 6
*/
Ogni “riga” può avere lunghezza diversa. Più flessibile delle matrici.
// Dichiarazione: [][] (doppia parentesi quadra)
int[][] arrayJagged = new int[3][];
// Inizializzazione di ogni riga (possono avere lunghezze diverse!)
arrayJagged[0] = new int[] { 1, 2, 3 }; // 3 elementi
arrayJagged[1] = new int[] { 4, 5 }; // 2 elementi
arrayJagged[2] = new int[] { 6, 7, 8, 9 }; // 4 elementi
// Accesso: [riga][colonna]
Console.WriteLine(arrayJagged[0][0]); // Output: 1
Console.WriteLine(arrayJagged[2][3]); // Output: 9
// Lunghezza di ogni riga
Console.WriteLine(arrayJagged[0].Length); // 3
Console.WriteLine(arrayJagged[1].Length); // 2
Console.WriteLine(arrayJagged[2].Length); // 4
| Caratteristica | Matrice [,] | Jagged Array [][] |
|---|---|---|
| Struttura | Righe tutte uguali | Righe di lunghezza variabile |
| Sintassi accesso | arr[i, j] |
arr[i][j] |
| Performance | Più veloce | Leggermente più lento |
| Uso principale | Dati tabulari regolari | Dati di lunghezza variabile |
📚 Collezioni Generiche
Le collezioni sono strutture dati più flessibili degli array, che possono
crescere o ridursi dinamicamente. C# offre diverse collezioni nel namespace
System.Collections.Generic.
💡 Perché Usare le Collezioni?
Gli array hanno dimensione fissa: se dichiari int[] numeri = new int[10];,
puoi contenere solo 10 elementi. Con le collezioni come List<int>,
puoi aggiungere o rimuovere elementi dinamicamente senza preoccuparti della dimensione!
List<T> – Lista Dinamica
List<T> è la collezione più utilizzata. È come un array che può
crescere automaticamente quando aggiungi elementi.
using System.Collections.Generic;
// Creazione (inizialmente vuota)
List<string> studenti = new List<string>();
// Aggiunta di elementi (alla fine)
studenti.Add("Marco");
studenti.Add("Anna");
studenti.Add("Luca");
// Lista: ["Marco", "Anna", "Luca"]
// Inserimento in posizione specifica
studenti.Insert(1, "Sara"); // Inserisce "Sara" all'indice 1
// Lista: ["Marco", "Sara", "Anna", "Luca"]
// Accesso agli elementi (come array)
Console.WriteLine(studenti[0]); // Output: Marco
// Modifica
studenti[2] = "Anna Maria";
// Numero di elementi
Console.WriteLine(studenti.Count); // Output: 4
List<string> studenti = new List<string>
{
"Marco", "Sara", "Anna", "Luca"
};
// Rimozione per valore
studenti.Remove("Luca"); // Rimuove "Luca"
// Lista: ["Marco", "Sara", "Anna"]
// Rimozione per indice
studenti.RemoveAt(0); // Rimuove il primo elemento
// Lista: ["Sara", "Anna"]
// Verifica presenza
bool contiene = studenti.Contains("Anna"); // true
bool contieneLuca = studenti.Contains("Luca"); // false
// Trova indice
int indice = studenti.IndexOf("Anna"); // 1
int nonTrovato = studenti.IndexOf("Paolo"); // -1 (non esiste)
// Pulizia completa
studenti.Clear(); // Svuota la lista
Console.WriteLine(studenti.Count); // 0
| Metodo | Descrizione | Esempio |
|---|---|---|
Add(item) |
Aggiunge alla fine | lista.Add("nuovo") |
Insert(index, item) |
Inserisce in posizione | lista.Insert(0, "primo") |
Remove(item) |
Rimuove per valore | lista.Remove("Mario") |
RemoveAt(index) |
Rimuove per indice | lista.RemoveAt(2) |
Contains(item) |
Verifica presenza | lista.Contains("Anna") |
IndexOf(item) |
Trova indice | lista.IndexOf("Luca") |
Clear() |
Svuota lista | lista.Clear() |
Count |
Numero elementi | lista.Count |
Dictionary<TKey, TValue> – Dizionario
Un dizionario è una collezione di coppie chiave-valore,
dove ogni chiave è unica. Perfetto per cercare valori rapidamente usando una chiave.
💡 Quando Usare un Dictionary
Immagina un registro studenti: invece di cercare il voto di “Marco” scorrendo tutta una lista,
con un dizionario puoi accedere direttamente: votiStudenti["Marco"].
Velocissimo! ⚡
using System.Collections.Generic;
// Creazione
Dictionary<string, int> votiStudenti = new Dictionary<string, int>();
// Aggiunta elementi - Metodo 1
votiStudenti.Add("Marco", 28);
votiStudenti.Add("Anna", 30);
// Aggiunta elementi - Metodo 2 (sintassi con [])
votiStudenti["Luca"] = 25;
votiStudenti["Sara"] = 27;
// Accesso ai valori tramite chiave
Console.WriteLine(votiStudenti["Anna"]); // Output: 30
// Modifica valore
votiStudenti["Marco"] = 29; // Aggiorna da 28 a 29
// Numero di elementi
Console.WriteLine(votiStudenti.Count); // 4
Dictionary<string, int> votiStudenti = new Dictionary<string, int>
{
{ "Marco", 28 },
{ "Anna", 30 },
{ "Luca", 25 }
};
// Verifica esistenza chiave
if (votiStudenti.ContainsKey("Marco"))
{
Console.WriteLine($"Voto di Marco: {votiStudenti["Marco"]}");
}
// TryGetValue - Metodo sicuro (consigliato!)
if (votiStudenti.TryGetValue("Paolo", out int voto))
{
Console.WriteLine($"Voto: {voto}");
}
else
{
Console.WriteLine("Paolo non trovato"); // Questo viene eseguito
}
// Rimozione
votiStudenti.Remove("Luca"); // Rimuove Luca
// Pulizia
votiStudenti.Clear();
Dictionary<string, int> votiStudenti = new Dictionary<string, int>
{
{ "Marco", 28 },
{ "Anna", 30 },
{ "Luca", 25 }
};
// Iterazione su tutte le coppie
foreach (var coppia in votiStudenti)
{
Console.WriteLine($"{coppia.Key}: {coppia.Value}");
}
// Output:
// Marco: 28
// Anna: 30
// Luca: 25
// Solo chiavi
foreach (string nome in votiStudenti.Keys)
{
Console.WriteLine(nome);
}
// Solo valori
foreach (int voto in votiStudenti.Values)
{
Console.WriteLine(voto);
}
Ogni chiave deve essere unica. Se provi ad aggiungere una chiave
già esistente con Add(), otterrai un’eccezione!
Dictionary<string, int> voti = new Dictionary<string, int>();
voti.Add("Marco", 28);
voti.Add("Marco", 30); // ❌ ERRORE! Chiave già esistente
// ✅ SOLUZIONE 1: Usa [] per sovrascrivere
voti["Marco"] = 30; // OK, aggiorna il valore
// ✅ SOLUZIONE 2: Verifica prima
if (!voti.ContainsKey("Marco"))
{
voti.Add("Marco", 28);
}
Stack<T> e Queue<T>
Stack (pila) e Queue (coda) implementano strutture dati speciali
per gestire elementi in ordine specifico.
L’ultimo elemento inserito è il primo ad uscire (come una pila di piatti).
using System.Collections.Generic;
Stack<string> pila = new Stack<string>();
// Push - Aggiunge in cima
pila.Push("Primo"); // ← Bottom
pila.Push("Secondo");
pila.Push("Terzo"); // ← Top
// Pop - Rimuove dalla cima
string ultimo = pila.Pop(); // "Terzo"
string penultimo = pila.Pop(); // "Secondo"
// Peek - Guarda la cima senza rimuovere
string top = pila.Peek(); // "Primo"
// Count
Console.WriteLine(pila.Count); // 1
Il primo elemento inserito è il primo ad uscire (come una fila alla cassa).
using System.Collections.Generic;
Queue<string> coda = new Queue<string>();
// Enqueue - Aggiunge in fondo
coda.Enqueue("Primo"); // ← Front
coda.Enqueue("Secondo");
coda.Enqueue("Terzo"); // ← Back
// Dequeue - Rimuove dal fronte
string primo = coda.Dequeue(); // "Primo"
string secondo = coda.Dequeue(); // "Secondo"
// Peek - Guarda il fronte senza rimuovere
string front = coda.Peek(); // "Terzo"
// Count
Console.WriteLine(coda.Count); // 1
- Stack: Undo/Redo, navigazione browser (cronologia), parsing espressioni
- Queue: Gestione task, stampa documenti, messaggi, BFS algoritmi
🔁 Iterazione su Array e Collezioni
Il modo più comune per processare tutti gli elementi di un array o collezione è usare un ciclo.
Quando serve l’indice o quando devi modificare gli elementi.
int[] numeri = { 10, 20, 30, 40, 50 };
// Iterazione classica
for (int i = 0; i < numeri.Length; i++)
{
Console.WriteLine($"Elemento {i}: {numeri[i]}");
}
// Modifica elementi (raddoppia tutti)
for (int i = 0; i < numeri.Length; i++)
{
numeri[i] *= 2;
}
// Risultato: [20, 40, 60, 80, 100]
Più semplice e leggibile quando non serve l'indice.
// Con array
int[] numeri = { 10, 20, 30, 40, 50 };
foreach (int numero in numeri)
{
Console.WriteLine(numero);
}
// Con List
List<string> città = new List<string>
{
"Roma", "Milano", "Napoli"
};
foreach (string città in città)
{
Console.WriteLine(città);
}
// Con Dictionary
Dictionary<string, int> voti = new Dictionary<string, int>
{
{ "Marco", 28 },
{ "Anna", 30 }
};
foreach (var coppia in voti)
{
Console.WriteLine($"{coppia.Key}: {coppia.Value}");
}
| Usa for quando... | Usa foreach quando... |
|---|---|
| Serve l'indice corrente | Serve solo leggere i valori |
| Devi modificare gli elementi | Vuoi codice più leggibile |
| Iteri all'indietro o con step | Iteri tutte le collezioni |
| Serve controllo fine iterazione | Non serve l'indice |
🚀 Esempio Completo: Sistema Gestione Studenti
Vediamo un esempio completo che utilizza List e Dictionary per gestire studenti e voti.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Console.WriteLine("=== SISTEMA GESTIONE STUDENTI ===\n");
// Lista studenti
List<string> studenti = new List<string>
{
"Marco Rossi",
"Anna Verdi",
"Luca Bianchi",
"Sara Neri",
"Paolo Gialli"
};
// Dizionario: studente → lista voti
Dictionary<string, List<int>> votiStudenti =
new Dictionary<string, List<int>>();
// Genera voti casuali per ogni studente
Random random = new Random();
foreach (string studente in studenti)
{
List<int> voti = new List<int>();
// 5 voti per studente
for (int i = 0; i < 5; i++)
{
voti.Add(random.Next(18, 31)); // 18-30
}
votiStudenti.Add(studente, voti);
}
// CALCOLO E VISUALIZZAZIONE MEDIE
Console.WriteLine("📊 Medie degli studenti:\n");
Dictionary<string, double> medie = new Dictionary<string, double>();
foreach (var coppia in votiStudenti)
{
string studente = coppia.Key;
List<int> voti = coppia.Value;
// Calcola media
double somma = 0;
foreach (int voto in voti)
{
somma += voto;
}
double media = somma / voti.Count;
medie.Add(studente, media);
// Mostra dettagli
Console.WriteLine($"👤 {studente}");
Console.Write(" Voti: ");
foreach (int voto in voti)
{
Console.Write($"{voto} ");
}
Console.WriteLine($"\n Media: {media:F2}");
Console.WriteLine();
}
// TROVA MIGLIORE STUDENTE
string miglioreStudente = "";
double mediaMax = 0;
foreach (var coppia in medie)
{
if (coppia.Value > mediaMax)
{
mediaMax = coppia.Value;
miglioreStudente = coppia.Key;
}
}
Console.WriteLine($"🏆 Studente con media più alta:");
Console.WriteLine($" {miglioreStudente} - Media: {mediaMax:F2}");
// TROVA PEGGIORE STUDENTE
string peggioreStudente = "";
double mediaMin = 30;
foreach (var coppia in medie)
{
if (coppia.Value < mediaMin)
{
mediaMin = coppia.Value;
peggioreStudente = coppia.Key;
}
}
Console.WriteLine($"\n⚠️ Studente che necessita supporto:");
Console.WriteLine($" {peggioreStudente} - Media: {mediaMin:F2}");
// STATISTICHE GENERALI
double mediaTotale = 0;
foreach (double media in medie.Values)
{
mediaTotale += media;
}
mediaTotale /= medie.Count;
Console.WriteLine($"\n📈 Statistiche:");
Console.WriteLine($" Totale studenti: {studenti.Count}");
Console.WriteLine($" Media classe: {mediaTotale:F2}");
Console.WriteLine($" Range: {mediaMin:F2} - {mediaMax:F2}");
}
}
- List<string> - Per memorizzare nomi studenti
- Dictionary<string, List<int>> - Mappa studente → voti
- Dictionary<string, double> - Cache delle medie calcolate
- foreach - Per iterare su collezioni
- Random - Per generare voti casuali
- Calcoli statistici - Media, min, max
💪 Esercizi Pratici
📝 Esercizio 1: Agenda Contatti
Crea un'agenda telefonica usando un Dictionary:
- Chiave: nome contatto (string)
- Valore: numero telefono (string)
- Implementa menu con: Aggiungi, Cerca, Rimuovi, Mostra tutti
- Gestisci contatti duplicati
- Permetti ricerca case-insensitive
📝 Esercizio 2: Gestione Inventario Negozio
Crea un sistema di inventario con:
- Dictionary per prodotti: nome → quantità
- Dictionary per prezzi: nome → prezzo
- Metodi: AggiungiProdotto, VendiProdotto, RifornisciProdotto
- Calcola valore totale inventario
- Mostra prodotti in esaurimento (quantità < 5)
- Dimensione fissa
- Accesso per indice (0-based)
- Array multidimensionali [,]
- Array jagged [][]
- List<T> - dinamica
- Dictionary<K,V> - chiave-valore
- Stack<T> - LIFO
- Queue<T> - FIFO
Lezione 12 del Corso C# Completo