Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Framework

Volem guardar els noms dels alumnes d’un grup. Amb un array, la mida queda fixada en crear-lo, i afegir un nom més obliga a copiar-ho tot en un array més gran:

String[] noms = new String[30];
noms[0] = "Aina";

Amb una col·lecció, la mida s’ajusta sola:

List<String> noms = new ArrayList<>();
noms.add("Aina");
noms.add("Bernat");

I si el que necessitem és que no hi hagi noms repetits, canviem el tipus i prou. El codi que omple la col·lecció no es toca:

Set<String> noms = new HashSet<>();
noms.add("Aina");
noms.add("Aina");   // no s'afegeix: la mida continua sent 1

Aquesta és la idea del framework de col·leccions (collections framework): un conjunt de tipus de la biblioteca estàndard per emmagatzemar i manipular grups d’objectes, on triar la garantia que necessitem (ordre, repeticions, accés per posició) és triar un tipus.

Les tres peces

El framework està format per tres coses:

  • Interfícies: diuen què garanteix una col·lecció, sense dir com ho aconsegueix. List promet ordre per posició; Set promet que no hi ha elements repetits.
  • Implementacions: les estructures de dades concretes que compleixen la promesa. ArrayList i LinkedList són dues maneres diferents de ser una List.
  • Algorismes: mètodes reutilitzables com ordenar o cercar, agrupats a la classe Collections. Són polimòrfics: el mateix Collections.sort() serveix per a qualsevol implementació de List, perquè està escrit contra la interfície i no contra una classe concreta.

D’aquí en surt una regla pràctica: declara la variable amb la interfície i tria la classe només en construir-la.

List<String> noms = new ArrayList<>();

Si més endavant una LinkedList resulta més adequada, canviem una paraula i la resta del programa no se n’assabenta.

Les interfícies

Aquestes són les interfícies que faràs servir per declarar variables:

InterfícieQuè garanteixExemple d’ús
CollectionUn grup d’elements que es pot recórrer i comptar. És el denominador comú de totesUn paràmetre que accepta qualsevol col·lecció
ListOrdre per posició, amb accés per índex; admet repeticionsLes línies d’un fitxer, en ordre
SetCap element repetit, segons equals()Els DNI que ja hem processat
SortedSetA més, recorregut per l’ordre natural dels elementsUna classificació sense empats
QueueElements que esperen a ser processats, habitualment en ordre FIFOLes tasques pendents d’un servidor
DequeEs pot afegir i treure pels dos extremsUna pila (LIFO) o una cua
MapParelles clau-valor, amb claus úniquesEl telèfon de cada persona
SortedMapA més, recorregut per l’ordre de les clausEsdeveniments indexats per data

Map no és una Collection i per això penja a part de la jerarquia: no conté elements, sinó parelles.

Java no proporciona cap implementació directa de Collection. És un tipus per parlar de col·leccions en general, sobretot als paràmetres dels mètodes: si el teu mètode només recorre els elements, demana una Collection i acceptarà llistes, conjunts i cues indistintament.

Ordre de recorregut garantit

Algunes col·leccions tenen un ordre de recorregut definit i d’altres no. Un HashSet no en té cap: el recorregut pot canviar entre execucions. Una List sí, i un LinkedHashSet també, perquè conserva l’ordre d’inserció.

Java 21 va agrupar les que en tenen sota tres interfícies seqüenciades: SequencedCollection, SequencedSet i SequencedMap. Aporten una manera uniforme d’accedir al primer i a l’últim element, i de recórrer la col·lecció al revés:

List<String> noms = new ArrayList<>(List.of("Aina", "Bernat", "Carla"));
noms.getFirst();     // "Aina"
noms.getLast();      // "Carla"
noms.reversed();     // vista invertida: [Carla, Bernat, Aina]

Abans calia recordar una manera diferent per a cada tipus (list.get(0), deque.peekFirst(), sortedSet.first()). Ara getFirst() funciona a totes.

Escollir la implementació

Per a cada interfície hi ha una implementació que és la tria per defecte, i alternatives que valen la pena en casos concrets:

InterfíciePer defecteAlternativa i quan
ListArrayListLinkedList si inserim o esborrem sovint pel principi
SetHashSetLinkedHashSet per conservar l’ordre d’inserció, TreeSet per recórrer ordenat
Queue, DequeArrayDequePriorityQueue si la sortida ha de seguir una prioritat
MapHashMapLinkedHashMap per l’ordre d’inserció, TreeMap per l’ordre de clau

Les implementacions Hash necessiten que els elements, o les claus, tinguin ben implementats equals() i hashCode(). Les implementacions Tree necessiten poder comparar els elements. Tots dos requisits es tracten a Comparació d’objectes.

Tipus parametritzats

A la documentació les interfícies apareixen amb un paràmetre entre angles. Per exemple, List<E>. Vol dir que una List no és una llista d’objectes qualssevol: és una llista d’elements d’un tipus que decidim en declarar-la, i el compilador ens avisa si hi posem res més.

List<String> noms = new ArrayList<>();
noms.add(42);   // error de compilació

Els tipus primitius no es poden fer servir com a paràmetre de tipus. Cal la classe embolcall (wrapper) corresponent: Integer per a int, Double per a double, Boolean per a boolean. La conversió és automàtica en els dos sentits, i se’n diu autoboxing:

List<Integer> edats = new ArrayList<>();
edats.add(17);          // int 17 passa a ser Integer
int primera = edats.get(0);   // i a la inversa

Aquest mecanisme, els genèrics, es tracta a fons a Genèrics.

Referències

Last change: , commit: 87dfa51