gianluca.aguzzi@unibo.it
angelo.filaseta@unibo.it
Questo materiale è ampiamente basato su quello realizzato dai Prof. Mirko Viroli e Roberto Casadei, che ringrazio.
Ogni errore riscontratovi è esclusiva responsabilità degli autori di questo documento.
Arrays
e Collections
Set
, List
e Map
java.util
Collection
– contenitore di elementi atomici; 2 sottotipi di collezioni
List
(sequenze)Set
(no duplicazioni)Map
– contenitore di coppie chiave-valore (memoria associativa)Collection
, List
, Set
, Map
, Iterator
, Comparable
ArrayList
, LinkedList
, HashSet
, TreeSet
, HashMap
Collections
, Arrays
Un meccanismo usato per gestire eventi ritenuti fuori dalla normale esecuzione (errori), ossia per dichiararli, lanciarli, intercettarli
for(int i: array) { ... }
java.lang.Iterable<X>
Iterable
e Iterator
Iterable
(contratto degli oggetti iterabili) ha un metodo per generare e restituire un (nuovo) Iterator
next()
, hasNext()
(e remove()
)coll
che implementa Iterable<T>
allora il foreach diventa:for(final T element: coll) { ... }
package java.lang;
import java.util.Iterator;
public interface Iterable<T> {
/**
* Returns an iterator over a set of elements of type T.
*
* @return an Iterator.
*/
Iterator<T> iterator();
}
package java.util;
public interface Iterator<E> {
boolean hasNext();
E next();
void remove(); // throws UnsupportedOperationException
}
package java.util;
public interface Collection<E> implements Iterable<E> { .. }
import java.util.*;
public class UseCollectionIterator {
public static void main(String[] s) {
// Uso la LinkedList
final List<Double> list = new LinkedList<>();
// Inserisco 50 elementi
for (int i = 0; i < 50; i++) {
list.add(Math.random());
}
// Stampo con un foreach
int count = 0;
for (final double d: list) {
System.out.println(count++ + "\t" + d);
}
// 0 0.10230513602737423
// 1 0.4318582138894327
// 2 0.5239222319032795
// ..
}
}
Collection<E>
(implementa Iterable<E>
)List
e Set
)Collection
, dal quale prende tutti gli elementiUnsupportedOperationException
c.contains(o)
) lavorano sulla base del metodo Object.equals()
da chiamare sugli elementi
Object
, influendo su alcuni metodi di Collection
Collection
: estratto dell’interfacciapublic interface Collection<E> extends Iterable<E> {
// Query Operations
int size(); // number of elements
boolean isEmpty(); // is the size zero?
boolean contains(Object o); // does it contain an element equal to o?
Iterator<E> iterator(); // yields an iterator
Object[] toArray(); // convert to array of objects
<T> T[] toArray(T[] a); // puts in `a`, or create new if too small
// Modification Operations
boolean add(E e); // adds e
boolean remove(Object o); // remove one element that is equal to o
// Bulk Operations
boolean containsAll(Collection<?> c); // contain all elements in c?
boolean addAll(Collection<? extends E> c); // add all elements in c
boolean removeAll(Collection<?> c); // remove all elements in c
boolean retainAll(Collection<?> c); // keep only elements in c
void clear(); // remove all element
// ...and other methods introduced in Java 8
}
Collection
Arrays.asList()
e Collection.toArray()
public class UseCollection {
public static void main(String[] s) {
// Uso una incarnazione, ma poi lavoro sull'interfaccia
final Collection<Integer> coll = new ArrayList<>();
coll.addAll(Arrays.asList(1, 3, 5, 7, 9, 11)); // var-args
System.out.println(coll); // [1, 3, 5, 7, 9, 11]
coll.add(13);
coll.add(15);
coll.add(15);
coll.remove(7);
System.out.println(coll); // [1, 3, 5, 9, 11, 13, 15, 15]
coll.removeAll(Arrays.asList(11, 13, 15));
coll.retainAll(Arrays.asList(1, 2, 3, 4, 5));
System.out.println(coll); // [1, 3, 5]
System.out.println(coll.contains(3)); // true
System.out.println(Arrays.toString(coll.toArray()));
var a = new Integer[2];
a = coll.toArray(a);
System.out.println(Arrays.deepToString(a));
}
}
.of(...)
e .copyOf(c)
su List
, Set
, …
Arrays.ofList
visto precedentemente, invece, crea una lista mutabile (e consente valori null
)public class UseFactories {
public static void main(String[] s) {
// Metodi statici di creazione per Set e List immutabili
final Set<Integer> set = Set.of(1, 2, 3, 4, 5, 6);
System.out.println(set);
final List<String> list = List.of("a", "b", "c", "a");
System.out.println(list);
final Set<String> set2 = Set.copyOf(list);
System.out.println(set2);
System.out.println(list.hashCode());
}
}
Set
e List
: introduzioneSet
Object.equals()
a dare true
null
Collection
List
La scelta fra queste due tipologie non dipende da motivi di performance, ma da quale modello di collezione serva!
Set
e List
: interfaccepublic interface Set<E> extends Collection<E> { }
public interface List<E> extends Collection<E> {
// Additional Bulk Operations
boolean addAll(int index, Collection<? extends E> c);
// Positional Access Operations
E get(int index); // get at position index
E set(int index, E element); // set into position index
void add(int index, E element); // add, shifting others
E remove(int index); // remove at position index
// Search Operations
int indexOf(Object o); // first equals to o
int lastIndexOf(Object o); // last equals to o
// List Iterators (enable traversal in both directions, modifications etc.)
ListIterator<E> listIterator(); // iterator from 0
ListIterator<E> listIterator(int index); // ..from index
// View
List<E> subList(int fromIndex, int toIndex);
}
void m(List<Integer> lst) {
final Set<String> names = // Dichiaro Set, non HashSet o altra implementazione
}
new
, a parte casi molto particolarifinal Set<String> names = new HashSet<>();
Set
Set
Object.equals()
)Set
possono avere prestazioni miglioriObject.hashCode()
come funzione di hash,
usata per posizionare gli elementi in uno store di elevate dimensioniLinkedHashSet
e HashSet
Linked
ha ordine di iterazione predicibile (quello degli elementi inseriti)TreeSet
Comparable
, oppure che venga fornito un Comparator
Linked
)HashSet
Object.hashCode()
)size
e
e.hashCode() % size
f
f.hashCode() % size
, usando Object.equals()
HashMap
, che approfondiremo in futuroHashSet
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
// Set vuoto, usa hashmap con capacità 16
public HashSet() {...}
// Set con elementi di c, usa hashmap del 25% più grande di c
public HashSet(Collection<? extends E> c) {...}
// Set vuoto
public HashSet(int initialCapacity, float loadFactor) {...}
// Set vuoto, loadFactor = 0.75
public HashSet(int initialCapacity) {...}
/* Gli altri metodi di Collection seguono... */
}
equals()
e hashCode()
Object
uguaglia lo stesso oggetto e l’hashing restituisce la posizione in memoria..equals()
e hashCode()
opportunamenteequals
devono avere lo stesso hashCode
HashSet
hashCode()
sfruttando il metodo di libreria
Objects.hash(Object...)
equals
djb2
, murmur3
)TreeSet<E>
Comparable
Integer
implementa Comparable<Integer>
Comparator
esterno fornito alla new
TreeSet
public interface Comparable<T> {
/*
* returns:
* 0 if this is equal to other
* positive int (1 is preferred) if this > other
* negative int (-1 is preferred) if this < o
*/
public int compareTo(T other);
}
Consente di definire una nozione di ordine naturale sugli oggetti di una classe
Pre-implementato in molte classi di libreria (più di 100!)
class Integer extends Number implements Comparable<Integer> { ... }
class String extends Object implements Comparable<String>, ... { ... }
Implementabile in modo personalizzato nelle nostre classi
public class Person implements Comparable<Person> {
private final int birthYear;
private final String surname;
/*
* Sort by year, and if they are equal by name
*/
public int compareTo(final Person other) {
final var byYear = this.birthYear > other.birthYear
? 1
: (this.birthYear < other.birthYear ? -1 : 0)
// Alternatively, just:
// final var byYear = Integer.compare(this.birthYear, other.birthYear)
return byYear == 0 ? this.name.compareTo(other.name) : byYear;
}
}
// String implements Comparable<String>
final var internal = new TreeSet<String>();
internal.add("zzz");
internal.add("lll");
internal.add("aaaaa");
internal.add("10");
internal.add("2");
// String uses lexicographical ordering!
System.out.println(internal); // [10, 2, aaaaa, lll, zzz]
Se a una collezione ordinata non è fornito un Comparator
esterno specifico,
allora sfrutterà l’ordinamento naturale del tipo degli elementi
(quindi se il tipo T
degli elementi non implementa Comparable<? super T>
, un’eccezione verrà sollevata)
Va passato un comparatore personalizzato (una classe che implementa Comparator
), due casi d’uso:
Comparable
public class StringByLength implements Comparator<String> {
// Sort by length, then lexicographically
public int compare(final String s1, final String s2) {
final var byLength = Integer.compare(s1.length(), s2.length());
return byLength == 0 ? s1.compareTo(s2) : byLength;
}
}
final var external = new TreeSet<String>(new StringByLength());
external.add("zzz");
external.add("lll");
external.add("aaaaa");
external.add("10");
external.add("2");
// Custom ordering!
System.out.println(external); // [2, 10, lll, zzz, aaaaa]
Comparator<? super E>
(Approfondimento)Data una classe SortedSet<E>
il suo comparatore ha tipo Comparator<? super E>
, perché non semplicemente Comparator<E>
?
Comparator
ha metodi che hanno E
solo come argomentoComparator<? super E>
è una generalizzazione di Comparator<E>
SimpleLamp
, e che questo sia usabile anche per tutte le specializzazioni successivamente costruite (è la situazione tipica)SortedSet<UnlimitedLamp>
deve poter usare il Comparator<SimpleLamp>
, ma questo è possibile solo grazie al suo tipo atteso Comparator<? super E>
List
List
public interface List<E> extends Collection<E> {
// Additional Bulk Operations
// aggiunge gli elementi in pos. index
boolean addAll(int index, Collection<? extends E> c);
// Positional Access Operations
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
// Search Operations
int indexOf(Object o); // basato su Object.equals
int lastIndexOf(Object o); // basato su Object.equals
// List Iterators
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
// View
List<E> subList(int fromIndex, int toIndex);
}
List
ArrayList
LinkedList
ArrayList
add()
è tempo costante ammortizzato, ossia, $n$ add si effettuano in $O(n)$Per migliorare le performance (e l’occupazione in memoria) in taluni casi l’utente esperto può usare funzioni aggiuntive
new
trimToSize()
e ensureCapacity()
per modifiche in itinereLinkedList
lista doppiamente linkata (che può essere traversata dall’inizio o dalla fine)
ArrayList
ArrayList
)Deque
,
usata per rappresentare una coda bidirezionale
(double-ended queue), potenzialmente con dimensione limitataLinkedList
: funzioni aggiuntive relative a code (e stack)public interface Queue<E> extends Collection<E> {
boolean offer(E e); // inserisce se c'è spazio
E poll(); // preleva se c'è il primo
E element(); // legge se c'è il primo, altrimenti eccezione
E peek(); // legge se c'è il primo, altrimenti null
}
// a "deque" (pronounced "deck") is a "double-ended queue"
public interface Deque<E> extends Queue<E> {
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E pollFirst();
E pollLast();
E getFirst();
E getLast();
E peekFirst();
E peekLast();
boolean removeFirstOccurrence(Object o);
boolean removeLastOccurrence(Object o);
void push(E e); // identical to addFirst()
E pop(); // identical to removeFirst()
}
LinkedList
: costruzionepublic class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
public LinkedList() {...}
public LinkedList(Collection<? extends E> c) {...}
}
UseLinkedList
public class UseLinkedList {
private static final String ELEMS = "A B C D E F G H I L M";
public static void main(String[] s) {
final LinkedList<String> llist = new LinkedList<>(Arrays.asList(ELEMS.split(" ")));
llist.addFirst("*");
llist.addLast("*");
llist.removeFirstOccurrence("*");
llist.removeLastOccurrence("*");
// bad performance
llist.add(llist.indexOf("L"), "K");
// better performance
final ListIterator<String> it = llist.listIterator();
while (it.hasNext()) {
if (it.next().equals("I")) {
it.add("J");
break;
}
}
final String[] str = llist.toArray(new String[0]);
System.out.println(Arrays.toString(str));
}
}
Arrays
e Collections
Arrays
e Collections
java.util.Arrays
binarySearch()
), ordinamento (quicksort, sort()
), copia (copyOf()
), riempimento (fill()
)toString
, equals
, hashCode
), anche ricorsivejava.util.Collections
Arrays
: qualche esempio di metodi
public class Arrays {
public static void sort(Object[] a, int fromIndex, int toIndex) {...}
public static <T> void sort(T[] a, Comparator<? super T> c) {...}
...
public static int binarySearch(int[] a, int key) {...}
public static int binarySearch(char[] a, char key) {...}
public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c) {...}
...
public static <T> List<T> asList(T... a) {...}
public static <T> T[] copyOfRange(T[] original, int from, int to) {...}
public static void fill(Object[] a, int from, int to, Object val) {...}
public static boolean deepEquals(Object[] a1, Object[] a2) {...}
public static int deepHashCode(Object a[]) {...}
public static String deepToString(Object[] a) {...}
public static String toString(long[] a) {...}
public static String toString(int[] a) {...}
public static String toString(Object[] a) {...}
}
UseArrays
: qualche esempio di applicazionepublic class UseArrays implements Comparator<Integer> {
public int compare(Integer a, Integer b) {
return b.compareTo(a); // ordine inverso
}
public static void main(String[] s) {
String[] array = new String[] { "a", "b", "c", "d", "e", "f" };
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f");
final Integer[] a = new Integer[20];
for (int i = 0; i < 20; i++) {
a[i] = (int) (Math.random() * 100);
}
System.out.println("rand: " + Arrays.toString(a));
Arrays.sort(a); // sort in ordine naturale
System.out.println("sort1: " + Arrays.toString(a));
Arrays.sort(a, new UseArrays()); // sort col comparator
System.out.println("sort2: " + Arrays.toString(a));
Arrays.fill(a, 10, 15, 0); // fill nel range
System.out.println("fill: " + Arrays.toString(a));
final Integer[][] b = new Integer[10][];
Arrays.fill(b, a); // fill di un array di array
System.out.println("recu: " + Arrays.deepToString(b));
}
}
Collections
: qualche esempio di metodi// nota: tutti metodi public static!!
public class Collections {
// ordinamenti e varie
<T extends Comparable<? super T>> void sort(List<T> list) {...}
<T> void sort(List<T> list, Comparator<? super T> c) {...}
<T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
<T> T min(Collection<? extends T> coll, Comparator<? super T> comp)
// modifiche
void reverse(List<?> list) {...}
void shuffle(List<?> list) {...}
<T> void fill(List<? super T> list, T obj) {...}
<T> void copy(List<? super T> dest, List<? extends T> src) {...}
// letture varie
int indexOfSubList(List<?> source, List<?> target) {...}
boolean disjoint(Collection<?> c1, Collection<?> c2) {...}
int frequency(Collection<?> c, Object o) {...}
// costruzioni di collezioni
<T> List<T> emptyList() {...}
<T> Set<T> emptySet() {...}
<T> List<T> nCopies(int n, T o) {...}
<T> Set<T> singleton(T o) {...}
<T> List<T> singletonList(T o) {...}
}
UseCollections
: qualche esempio di applicazionepublic class UseCollections {
public static void main(String[] s) {
final List<Integer> list = Arrays.asList(new Integer[] { 0, 1, 2, 3, 4 });
final Set<List<Integer>> set = new HashSet<>();
for (int i = 0; i < 5; i++) {
final List<Integer> l2 = new ArrayList<>(list);
Collections.shuffle(l2);// permuto
if (!set.contains(l2)) { // no duplicazione!
set.add(l2); // aggiungo
}
}
System.out.println("shuf: " + set); // [[4,1,2,3,0],[3,1,4,0,2],..
int ct = 0;
for (final List<Integer> l : set) {
Collections.fill(l, ct++);
}
System.out.println("inc: " + set); // [[0,0,0,0,0],[1,1,1,1,1],..
System.out.println("cop: " + Collections.nCopies(5, list));
// [[0,1,2,3,4],[0,1,2,3,4],..
}
}
java.util.Map
Map
public interface Map<K,V> {
// Query Operations
int size();
boolean isEmpty();
boolean containsKey(Object key); // usa Object.equals
boolean containsValue(Object value); // usa Object.equals
V get(Object key); // accesso a valore
// Modification Operations
V put(K key, V value); // inserimento chiave-valore
V remove(Object key); // rimozione chiave(-valore)
// Bulk Operations
void putAll(Map<? extends K, ? extends V> m);
void clear(); // cancella tutti
// Views
Set<K> keySet(); // set di valori
Collection<V> values(); // collezione di chiavi
... // qualche altro
}
Map
Map<K,V>
K
in V
Object.equals
)HashMap
HashSet
di coppie Key
, Value
LinkedHashMap
TreeMap
TreeSet
di coppie Key
, Value
TreeSet
public class UseMap {
public static void main(String[] args) {
// Uso una incarnazione, ma poi lavoro sull'interfaccia
final Map<Integer, String> map = new HashMap<>();
// Una mappa è una funzione discreta
map.put(345211, "Bianchi");
map.put(345122, "Rossi");
map.put(243001, "Verdi");
System.out.println(map); // {345211=Bianchi, 243001=Verdi, 345122=Rossi}
map.put(243001, "Neri"); // Rimpiazza Verdi
final Map<String,Integer> map2 = Map.of("foo", 5, "bar", 7);
// modo prestante per accedere alle coppie
for(final Map.Entry<String,Integer> kv : map2.entrySet()) {
System.out.println("Chiave: " + kv.getKey() + " Valore: " + kv.getValue());
}
// modo poco prestante per accedere alle coppie chiave-valore
for (final Integer i : map.keySet()) {
System.out.println("Chiave: " + i + " Valore: " + map.get(i));
}
// modo prestante per accedere ai soli valori
for (final String s : map.values()) {
System.out.println("Valore: " + s);
}
}
}