Collections en Java
1.
Structures de données
C'est l'organisation
efficace d'un ensemble de données, sous la forme de tableaux, de listes, de
piles etc. Cette efficacité réside dans la quantité mémoire utilisée pour
stocker les données, et le temps nécessaire pour réaliser des opérations sur
ces données.
2. Collections & Java
Une collection gère un
groupe d'un ensemble d'objets d'un type donné ; ou bien c'est un objet qui sert
à stocker d'autres objets. Dans les
premières versions de Java, les collections étaient représentées par les
"Array","Vector","Stack" etc. Puis avec Java 1.2
(Java 2), est apparu le frameWork de collections qui tout en gardant les
principes de bases, il a apporté des modifications dans la manière avec laquelle
ces collections ont été réalisées et hiérarchisées.
Tout en
collaborant entre elles, ces collections permettent de réaliser dans des
catégories de logiciels des conceptions réutilisables.
3. Collections Framwork de Java
Réparties en deux groupes:
3.1.
Interfaces
Organisées en deux
catégories: Collection & Map.
-
Collection: un groupe d'objets où la duplication peut-être autorisée.
-
Set:
est ensemble ne contenant que des valeurs et ces valeurs ne sont pas
dupliquées. Par exemple l'ensemble A = {1,2,4,8}. Set hérite donc de Collection, mais n'autorise pas la
duplication. SortedSet est un Set trié.
-
List: hérite aussi de collection, mais autorise la duplication. Dans cette
interface, un système d'indexation a été introduit pour permettre l'accès
(rapide) aux éléments de la liste.
-
Map:
est un groupe de paires contenant une clé et une valeur associée à cette clé.
Cette interface n'hérite ni de Set ni de Collection. La raison est que
Collection traite des données simples alors que Map des données composées
(clé,valeur). SortedMap est un Map trié.
3.2. Implémentations
Le framework fournit les
implémentations suivantes des différentes interfaces:
|
Classes d'implémentations
|
|||||
|
Table
de Hachage
|
Table de Hachage et Liste Chaînée
|
Tableau de taille variable
|
Arbre balancé
|
Liste chaînée
|
|
Interfaces
|
Set
|
HashSet
|
LinkedHashSet
|
|
TreeSet
|
|
List
|
|
|
ArrayList
|
|
LinkedList
|
|
Map
|
HashMap
|
LinkedHashMap
|
|
TreeMap
|
|
Par contre, il
n'y a pas d'implémentation de l'interface Collection. Pour Set et Map
l'implémentation est soit sous la forme d'une table de hachage
(HashSet/HashMap), sous la forme d'un arbre (TreeSet/TreeMap) ou bien sous la
forme d’une table de hachage jumelée à une liste chaînée
(LinkedHashSet/LinkedHashMap). Pour la liste: soit sous la forme de tableau
(ArrayList) ou une liste chaînée (LinkedList).
4. Algorithmes
Sont utilisés pour traiter
les éléments d'un ensemble de données. Ils définissent une procédure
informatique, par exemple: tris, recherche etc.
5. Itérateurs
Fournissent aux algorithmes
un moyen pour parcourir une collection du début à la fin. Ce moyen permet de
retirer donc à la demande des éléments donnés de la collection.
6. Description des interfaces
6.1. Collection
public interface Collection<E> extends Iterable<E> {
// Basic Operations
int size();
boolean isEmpty();
boolean contains(Object
element);
boolean add(E element); // Optional
boolean remove(Object
element); // Optional
Iterator iterator();
// Bulk Operations
boolean containsAll(Collection<?>
c);
boolean addAll(Collection<?
extends E> c); // Optional
boolean
removeAll(Collection<?> c);
// Optional
boolean
retainAll(Collection<?> c);
// Optional
void clear(); // Optional
// Array Operations
Object[] toArray();
<T> T[]
toArray(T[] a);
}
Les interfaces contiennent
des méthodes optionnelles. Cette approche permet de traiter les collections
particulières sans que nous soyons dans l'obligation de définir les méthodes
optionnelles. Ces méthodes optionnelles sont définies qu'en cas de besoin. Un
Set non modifiable n'a pas besoin de la méthode add, puisque nous ne pouvons
pas le modifier! Par contre, puisqu’il
implémente l’interface, il doit tout de même la redéfinir, dans ce cas la méthode
génère une exception de type UnsupportedOperationException.
Il y a des opérations
réalisées sur un seul objet ou bien sur une collection (un ensemble d'objets).
add (remove) permet
d'ajouter (resp. de retirer) un élément. Quand à addAll (removeAll) permet
d'ajouter (resp. de retirer même si les éléments sont dupliqués dans la
collection originale) une collection.
contains (containsAll) permet
de vérifier si un objet (resp. les éléments d'une collection) est présent dans
la collection.
size, isEmpty et clear,
permettent respectivement de donner la taille de la collection, de vérifier si
la collection est vide et finalement d'effacer le contenu de la collection.
retainsAll se comporte comme
le résultat de l'intersection de deux ensembles. Si A={1,2,5,8} et B={3,8}
alors A = {8}.
equals permet de tester si
deux objets sont égaux.
hashCode retourne le code de
hachage calculé pour la collection.
toArray retourne les
éléments de la collection sous le format d'un tableau.
toArray(T[] a) permet de préciser le type du tableau à retourner. Si le tableau est grand les éléments sont rangés dans ce tableau, sinon un nouveau tableau est crée pour recevoir les éléments de la collection. ex : String[] a = c.toArray(new String[0]);
L'interface collection est
dotée d'une instance d'une classe qui implante l'interface Iterator. C'est
l'outil utilisé pour parcourir une collection. L'interface Iterator contient ce
qui suit:
public interface Iterator<E> {
boolean hasNext();
E next();
void remove(); // Optional
}
hasNext permet de vérifier
s'il y a un élément qui suit.
next permet de pointer
l'élément suivant et retourne une référence du bon type, donc, pas besoin de
conversion de type (Type Cast).
remove permet de retirer
l'élément courant.
Collection<String> collection = new
ArrayList<String>();
...
Iterator<String> iterator =
collection.iterator();
while (iterator.hasNext()) {
String element
= iterator.next();
if
(removalCheck(element)) {
iterator.remove();
}
}
Les collections vues comme des ensembles
réalisent les 3 opérations mathématiques sur des ensembles:
union:
add et addAll
intersection:
retainAll
différence: remove et
removeAll
6.2. Set
C'est une interface
identique à celle de Collection. Trois implémentations possibles:
TreeSet: les éléments sont rangés de manière
ascendante.
HashSet: les éléments sont rangés suivant une
méthode de hachage.
LinkedHashSet: Comme HashSet
mais éléments accessibles en ordre d’insertion.
import java.util.*;
public class SetExample {
public
static void main(String args[]) {
Set<String> set = new
HashSet<String>(); // Une table de Hachage
set.add("Bernadine");
set.add("Elizabeth");
set.add("Gene");
set.add("Elizabeth");
set.add("Clara");
System.out.println(set);
Set<String> setTrie = new TreeSet<String>(set); // Un Set
trié
System.out.println(setTrie);
}
}
[Gene, Clara, Bernadine, Elizabeth]
[Bernadine, Clara, Elizabeth, Gene]
6.2. List
Liste est une collection
ordonnée. Elle permet la duplication des éléments. L'interface est renforcée
par des méthodes permettant d'ajouter ou de retirer des éléments se trouvant à
une position donnée. Elle permet aussi de travailler sur des sous listes. On
utilise le plus souvent des ArrayList sauf s'il y a insertion d'élément(s) au
milieu de la liste. Dans ce cas il est préférable d'utiliser une LinkedList
pour éviter ainsi les décalages.
public interface List<E> extends Collection<E> {
// Positional Access
E get(int index);
E set(int index, E element); // Optional
boolean add(E element); // Optional
void add(int index, E element); // Optional
E remove(int index); // Optional
abstract boolean addAll(int index, Collection<? extends E> c);//Opt.
// Search
int indexOf(Object o);
int lastIndexOf(Object o);
// Iteration
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
// Range-view
List<E> subList(int from, int to);
}
Les méthodes de l'interface
List permettent d'agir sur un élément se trouvant à un index donné ou bien un
ensemble d'éléments à partir d'un index donné dans la liste.
get (remove) retourne (resp.
retirer) l'élément se trouvant à la position index.
set (add & addAll)
modifie (resp. ajouter) l'élément (resp. un seul ou une collection) se trouvant
à la position index.
indexOf (lastIndexOf)
recherche si un objet se trouve dans la liste et retourner son (resp. son
dernier) index.
subList permet de créer un
sous liste d'une liste.
Pour parcourir une liste, il
a été défini un itérateur spécialement pour la liste.
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void remove(); // Optional
void set(E o); // Optional
void add(E o); // Optional
}
permet donc de parcourir la
liste dans les deux directions et de modifier un élément (set) ou d'ajouter un
nouveau élément.
List<Type> list = ...;
for (ListIterator<Type> i = list.listIterator(list.size());
i.hasPrevious(); ) {
Type t = i.previous();
...
}
ou encore :
List<Type> list = ...;
ListIterator<Type> iterator =
list.listIterator(list.size());
while (iterator.hasPrevious()) {
Type
element = iterator.previous();
...
}
hasNext permet de vérifier
s'il y a un élément qui suit.
next permet de pointer
l'élément courant.
nextIndex retourne l'index
de l'élément courant.
Pour les sous listes, elles
sont extraites des listes de fromIndex (inclus) à toIndex (non inclus). Tout
changement sur les sous listes affecte la liste de base, et l'inverse provoque
un état indéfini s'il y a accès à la sous liste.
import java.util.*;
public class ListExample {
public
static void main(String args[]) {
List<String>
list = new ArrayList<String>();
list.add("Bernadine");
list.add("Elizabeth");
list.add("Gene");
list.add("Elizabeth");
list.add("Clara");
System.out.println(list);
System.out.println("2: " + list.get(2));
System.out.println("0: " + list.get(0));
LinkedList<String> queue = new LinkedList<String>();
queue.addFirst("Bernadine");
queue.addFirst("Elizabeth");
queue.addFirst("Gene");
queue.addFirst("Elizabeth");
queue.addFirst("Clara");
System.out.println(queue);
queue.removeLast();
queue.removeLast();
System.out.println(queue);
}
}
[Bernadine, Elizabeth, Gene, Elizabeth, Clara]
2: Gene
0: Bernadine
[Clara, Elizabeth, Gene, Elizabeth, Bernadine]
[Clara,
Elizabeth, Gene]
6.3. Map
C'est un ensemble de paires,
contenant une clé et une valeur (en réalité, nous pouvons associer plusieurs
valeurs. Dans ce cas la, nous sommes en présence d'une multimap … à voir en
démo.).
Deux clés ne peuvent être
égales au sens de equals.
L'interface interne Entry
permet de manipuler les éléments d'une paire comme suit:
public interface Map.Entry<K,V>
{
K getKey();
V getValue();
V setValue(V value);
}
getKey & getValue
retournent respectivement la clé et la valeur associée à cette clé. setValue
permet de modifier une valeur d'une paire. Remarque: faire attention de
ne pas modifier directement la valeur associée à une clé. Pour le faire,
retirer l'ancienne clé (et donc sa valeur aussi) et ajouter une nouvelle clé
(avec cette nouvelle valeur).
public interface Map <K,V> {
// Basic Operations
V put(K key, V value);
V get(Object key);
V remove(Object key);
boolean containsKey(Object key);
boolean containsValue(Object value);
int size();
boolean isEmpty();
// Bulk Operations
void putAll(Map<? extends K,? extends V> t);
void clear();
// Collection Views
public Set<K> keySet();
public Collection<V> values();
public Set<Map.Entry<K,V>> entrySet();
// Interface for entrySet elements
public interface Entry {
K getKey();
V getValue();
V setValue(V value);
}
}
values retourne les valeurs
sous la forme d’une Collection.
keySet et entrySet retournent,
respectivement, un ensemble (Set) de clés et un ensemble (set) d'Entry.
Ceci permet donc d'itérer
sur les Map comme suit:
si m est un HashMap alors:
// sur les clés
for (Iterator<TypeCles>
i = m.keySet().iterator();i.hasNext();)
System.out.println(i.next());
// sur les valeurs
for (Iterator<TypeVal>
i = m.values().iterator();i.hasNext();)
System.out.println(i.next());
// sur la paire clé/valeur
for (Map.Entry<TypeCles, TypeVals> e : m.entrySet())
System.out.println(e.getKey() + ": " + e.getValue());
import java.util.*;
public class Freq {
public static
void main(String args[]) {
Map<String, Integer> m1 = new
HashMap<String, Integer>();
Map<String,Integer> m2 = new
LinkedHashMap<String, Integer>();
//
Initialize frequency table from command line
for (String
a : args) { // Utilisation de for ... each
Integer
freq = m1.get(a);
m1.put(a,
(freq == null ? 1 : freq + 1)); //1 est enrobé dans
m2.put(a, (freq == null ? 1 : freq + 1)); // dans un Integer
}
System.out.println(m1.size() + " distinct words:");
System.out.println("Avec HashSet " + m1);
System.out.println("Avec LinkedHashSet " + m2);
}
}
exemples
de résultats
=>
java Freq Je suis le chef le grand chef
5
distinct words:
Avec
HashSet {grand=1, chef=2, suis=1, Je=1, le=2}
Avec
LinkedHashSet {Je=1, suis=1, le=2, chef=2, grand=1}
7. Description des algorithmes
L'ensemble des algorithmes
manipulant les collections se trouve dans la classe Collections (à ne pas
confondre avec l'interface Collection). Ces méthodes ont été définies
statiques.
Des algorithmes qui ne s'appliquent
que sur des listes:
Trier:
trier une liste :
public static <T extends Comparable<? super T>> void sort(List<T> list)
trie une liste en utilisant
un comparateur.
public static <T> void sort(List<T> list, Comparator<? super T> c);
Mélanger:
public static void shuffle(List<?> list);
mélange les éléments de manière aléatoire.
Manipuler:
void reverse(List<?> list) ; inverse les
éléments de la liste.
public static <T> void fill(List<? super T> list,
T obj); initialise les éléments de la liste avec obj.
public static <T> void copy(List<? super T> dest, List<? extends T> src); copy une liste src dans une liste dest.
Rechercher:
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key);
une recherche binaire d'un
élément.(key).
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key. Comparator<? super T> c)); d'un élément en utilisant un comparateur.
Des algorithmes qui
s'appliquent sur toutes les collections:
effectuer des recherches
extrêmes:
min, max etc.
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)
public static
<T extends Object & Comparable<? super T>> T max (Collection<? extends
T> coll)
8. Interface Comparable
Un algorithme extrêmement
rapide et stable (les éléments équivalents ne sont pas réordonnés) est utilisé
pour trier la liste en utilisant l'ordre naturel du type. Le tri ne peut avoir
lieu que si les classes implémentent la méthode Comparable, ce qui n'est
toujours pas le cas. Cette classe contient une seule méthode compareTo:
public interface Comparable<T> {
public int compareTo(T obj);
}
Cette méthode retourne:
- entier positif si l'objet
qui fait l'appel est plus grand que obj,
- zéro s'ils sont
identiques,
- négatif si l'objet qui
fait l'appel est plus petit que obj.
Dans le cas d'une classe qui
n'implante pas la classe Comparable, ou bien vous voulez spécifier un autre
ordre, vous devez implanter l'interface Comparator. Cette dernière permet de
comparer deux éléments de la collection. Pour trier, nous passons une instance
de cette classe à la méthode Sort().
public interface Comparator<T> {
int compare(T o1, T o2);
}
class
CaseInsensitiveComparator implements Comparator<String> {
public int compare(String element1,String
element2) {
String lowerE1 = element1.toLowerCase();
String lowerE2 = element2.toLowerCase();
return lowerE1.compareTo(lowerE2);
}
}
Aucun commentaire:
Enregistrer un commentaire