4. Interface Set

4.1. Notion de Set

L'interface Set modèlise un ensemble d'objets dans lequel on ne peut pas trouver de doublons. Cette notion impose que l'égalité entre objets soit définie, ce qui est le cas, puisque tout objet Java dispose d'une méthode equals(). À la différence des interfaces List et Collection, l'ajout d'un élément dans un Set peut donc échouer, si cet élément s'y trouve déjà. C'est la raison pour laquelle la méthode add(T t) retourne un booléen. Ce booléen est toujours à true dans une collection ou une liste, dans le cas des Set il arrive qu'il soit à false. Enfin notons qu'un Set ne peut pas contenir plus d'un objet nul. Certaines implémentations empêchent l'ajout d'objets nuls dans un Set. L'implémentation fournie par l'API Java, HashSet, utilise, pour des raisons de performances, les codes de hachage des objets ajoutés. Cela est légal, puisque deux objets égaux doivent avoir même code de hachage. Respecter ce contrat s'avère donc crucial ici : s'il ne l'est pas, le HashSet ne pourra pas fonctionner correctement. De plus, on prendra garde que les champs utilisés pour la construction du code de hachage, et pour la méthode equals(), ne sont pas modifiés une fois l'objet ajouté à un HashSet. Effectivement, le HashSet ne pourrait pas détecter ces modifications, et ne mettra pas à jour le code de hachage qu'il utilise en interne. Là encore le fonctionnement du HashSet sera perturbé.

4.2. Implémentations HashSet et LinkedHashSet

L'API standard nous propose deux implémentations pour cette interface : HashSet et LinkedHashSet.

4.2.1. Classe HashSet

Cette classe fonctionne avec une table de hachage, dont les clés sont les codes de hachage des objets ajoutés, et les valeurs les objets eux-mêmes. Il est donc crucial que les méthodes equals() et hashCode() des objets ajoutés à un HashSet respectent leur contrat. Cette implémentation offre des performances constantes pour les opérations add(T t), remove(T t), contains(T t) et size(). Le temps pris par ces opérations n'augmente donc pas avec le cardinal du HashSet. L'itération sur les éléments d'un HashSet est, elle, proportionnel à ce cardinal, et à la capacité totale du HashSet. On veillera donc à ne pas itérer sur de tels ensembles s'ils contiennent beaucoup d'éléments. Notons que l'itération sur les éléments d'un HashSet se fait dans un ordre qui n'est pas prévisible, tout comme pour une collection classique.

4.2.2. Classe LinkedHashSet

La classe LinkedHashSet est une extension de HashSet. Elle offre donc les mêmes fonctionnalités et la même sémantique. Cette classe entretient de plus une double liste chaînée dont les éléments sont les entrées de la table de hachage qui stocke les objets. Cela permet de conserver l'ordre dans lequel on parcourt les objets lors d'une itération. Le fait d'ajouter à un LinkedHashSet un objet qui s'y trouve déjà ne change pas la place de cet objet dans l'ensemble. L'entretien de cette double liste est une surcharge mineure, qui n'affecte que très peu les opérations add(T t), remove(T t), contains(T t) et size(). En revanche, itérer ne dépend pas de la capacité de cette table, uniquement de son cardinal.

4.3. Exemples d'utilisation

Voyons un exemple d'utilisation d'un Set.

Exemple 8. Utilisation d'un Set

// dans une méthode main
 // création du set
Set<String> set =  new HashSet<String>() ;

 // ajout d'élément
System.out.println("J'ajoute un : " + set.add("un")) ;
System.out.println("J'ajoute deux : " + set.add("deux")) ;

 // ajout d'un doublon : échec
System.out.println("J'ajoute encore un : " + set.add("un")) ;

 // affichage de la taille du set
System.out.println("Taille du set : " + set.size()) ;

L'exécution de ce code affiche le résultat suivant.
J'ajoute un : true
J'ajoute deux : true
J'ajoute encore un : false
Taille du set : 2
Java API avancées
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
API Collection
1. Introduction
2. Interface Collection
2.1. Notion de Collection
2.2. Détail des méthodes disponibles
2.3. Interface Iterator
2.4. Implémentation, exemples d'utilisation
3. Interface List
3.1. Notion de List
3.2. Détail des méthodes disponibles
3.3. Interface ListIterator
3.4. Implémentations, exemples d'utilisation
4. Interface Set
4.1. Notion de Set
4.2. Implémentations HashSet et LinkedHashSet
4.3. Exemples d'utilisation
5. Interface SortedSet
5.1. Notion de SortedSet
5.2. Détails des méthodes disponibles
5.3. Exemples d'utilisation
6. Interface NavigableSet
6.1. Notion de NavigableSet
6.2. Détails des méthodes disponibles
6.3. Exemple d'utilisation
7. Interfaces Queue et Deque
7.1. Notion de file d'attente
7.2. Détail des méthodes disponibles
7.3. Utilisation des interfaces Queue et Deque
8. Tables de hachage
8.1. Notion de table de hachage
8.2. Interface Map
8.3. Interface Map.Entry
8.4. Interface SortedMap
8.5. Interface NavigableMap
8.6. Implémentations
8.7. Exemples d'utilisation
9. Classes utilitaires Collections et Arrays
9.1. Introduction
9.2. Classe Arrays
9.3. Classe Collections
Génériques
1. Introduction
2. Un premier exemple
2.1. Une première classe générique
2.2. Une première méthode générique
3. Contraindre un type générique
3.1. Problème posé
3.2. Contraindre un type générique
4. Implémentation des génériques
4.1. Type erasure
4.2. Types génériques et casts
4.3. Type générique et exception
4.4. Construction d'une instance générique
4.5. Génériques et membres statiques
4.6. Collisions de méthodes génériques
4.7. Implémentation de plusieurs types identiques
5. Type <?>
5.1. Introduction
5.2. Type ? extension d'un type
5.3. Type ? super-type d'un type
Expressions régulières
1. Introduction
2. Mise en œuvre des expressions régulières
2.1. Fonctionnement d'une regexp
2.2. Fonctionnement de l'API en Java
2.3. Un premier exemple
2.4. Classe Pattern
2.5. Classe Matcher
2.6. Utilisation des méthode find() et group()
2.7. Méthodes de remplacement
2.8. Sélection de régions
3. Syntaxe des expressions régulières
3.1. Notion de classe
3.2. Étude d'un cas réel
3.3. Recherche d'un mot précis
3.4. Recherche de deux mots précis
3.5. Recherche d'un mot commençant par une lettre donnée
3.6. Cas de mots comportant des caractères accentués
3.7. Recherche sur les lignes
Introspection
1. Introduction
2. La classe Class
2.1. Utilisation de Class
2.2. Méthodes disponibles
2.3. Remarque sur la propriété Accessible
2.4. Type d'une classe
2.5. Création d'une instance à partir d'un objet Class
2.6. Cas des énumérations
3. Les classes Method et Constructor
3.1. Utilisation de Method
3.2. Utilisation de Constructor
3.3. Méthodes disponibles
3.4. Invocation d'une méthode par introspection
4. La classe Field
4.1. Utilisation de Field
4.2. Méthodes disponibles
4.3. Accès à un champ par introspection
5. La classe Modifier
Programmation concurrente
1. Introduction
2. Lançons nos premiers threads
2.1. Introduction
2.2. Un premier thread, extension de Thread
2.3. Un deuxième thread, implémentation de Runnable
2.4. Remarque sur la méthode Thread.sleep(long)
2.5. Arrêter un thread
3. Concurrence d'accès
3.1. Notion d'état
3.2. Exemple de concurrence d'accès sur un état
3.3. Analyse de la concurrence d'accès
3.4. Solution au problème
3.5. Champs volatile
4. Synchronisation
4.1. Définition d'un bloc synchronisé
4.2. Fonctionnement d'un bloc synchronisé
4.3. Notion de deadlock
4.4. Bonnes pratiques pour la synchronisation de threads
5. Opérations atomiques
5.1. Atomicité d'une opération
5.2. Solutions disponibles
5.3. Variables atomiques
6. Collections synchronisées et concurrentes
6.1. Introduction
6.2. Position du problème
6.3. Solutions proposées
7. Files d'attente
7.1. Introduction, pattern producteur / consommateur
7.2. Interface BlockingQueue<E>
7.3. Implémentations de BlockingQueue
7.4. Exemple de producteur / consommateur
7.5. Arrêter un producteur / consommateur : pilule empoisonnée
8. Classes utilitaires de l'API Concurrent
8.1. Introduction
8.2. Énumération TimeUnit
8.3. Interface Callable<V>
8.4. Interfaces Future<V> et RunnableFuture<V>
8.5. Interface ScheduledFuture<V> et RunnableScheduledFuture<V>
9. Pattern executor
9.1. Notion de réserve de threads
9.2. Interface Executor
9.3. Interface ExecutorService
9.4. Interface ScheduledExecutorService
9.5. Classe Executors
9.6. Pattern de lancement de tâches
10. Classes de contrôle d'accès
10.1. Introduction
10.2. Interfaces Lock et ReadWriteLock
10.3. Notion de verrou réentrant
10.4. Classe RentrantLock
10.5. Classe ReadWriteRentrantLock
11. Sémaphores, barrières et latches
11.1. Introduction
11.2. Notion de sémaphore, classe Semaphore
11.3. Notion de latch, classe CountDownLatch
11.4. Notion de barrière, classe CyclicBarrier