Collections en Java

Université de Toulon

LIS UMR CNRS 7020

2024-11-27

Source
Branch
  • develop (e3d3004)
  • 2024/09/23 13:53:54
Java
  • OpenJDK Temurin-21.0.5+11
  • Apache Maven 3.9.9
Docker
  • Client: 27.3.1 - buildx v0.18.0 - compose v2.30.3
  • Server: 27.3.1

L’intérêt des collections

  • Importance des structures de données :
    • Choisir des structures de données adaptées est crucial lors du développement.
    • Permet de stocker, parcourir et retrouver des objets efficacement.
  • Types de structures de données :
    • Tableaux, listes, arbres, tables de hachage, etc.
  • Ressources supplémentaires :

Les collections

  • Définition : En Java, une collection est un objet qui permet de contenir des références vers d’autres objets.
  • Comparaison avec les tableaux :
    • Les tableaux sont une structure de données qui contient des références.
    • Les collections offrent plus de flexibilité et de fonctionnalités.
  • Le JDK propose :
    • Interfaces : Définissent les grands types de collections (List, Set, Map, etc.).
    • Classes abstraites : Implantent partiellement ces interfaces (AbstractList, AbstractSet, etc.).
    • Classes concrètes : Précisent les implantations (ArrayList, HashSet, HashMap, etc.).
    • Paquetage java.util : Contient toutes les interfaces et classes de collections.
    • Itérateurs : Mécanisme de parcours général des collections.
    • Outils de tri et de recherche : Méthodes utilitaires pour trier et rechercher dans les collections.

Une hiérarchie d’Interfaces

En Java, les collections se distinguent en plusieurs types principaux :

Des classes abstraites

  • Classes abstraites : Un ensemble de classes abstraites implémente les interfaces des collections et prend en charge les parties communes.
    • Exemples :
      • AbstractCollection, AbstractList, AbstractQueue, AbstractSequentialList, AbstractSet, AbstractMap
  • Classes concrètes : Ces classes abstraites sont réalisées par des classes concrètes qui implémentent les fonctionnalités spécifiques.
    • Exemples :
      • ArrayList
        • Algorithme : Basée sur un tableau dynamique.
        • Complexité :
          • Accès par index : Θ(1)
          • Insertion/Suppression (à la fin) : Θ(1)
          • Insertion/Suppression (au milieu) : Θ(n)
      • TreeSet
        • Algorithme : Basée sur un arbre rouge-noir.
        • Complexité :
          • Insertion : Θ(log n)
          • Suppression : Θ(log n)
          • Recherche : Θ(log n)
      • HashMap
        • Algorithme : Basée sur une table de hachage.
        • Complexité :
          • Insertion : Θ(1) en moyenne
          • Suppression : Θ(1) en moyenne
          • Recherche : Θ(1) en moyenne
      • TreeMap
        • Algorithme : Basée sur un arbre rouge-noir.
        • Complexité :
          • Insertion : Θ(log n)
          • Suppression : Θ(log n)
          • Recherche : Θ(log n)

Hierarchie

Structures de Données Classiques

  • Hiérarchie de classes : Organisation des structures de données en classes et interfaces.
  • Interfaces communes : Flexibilité et réutilisabilité accrues.
  • Efficacité : Stockage, parcours et manipulation optimisés des collections d’objets.
  • Interface List
    • Tableaux de taille variable : ArrayList
      • Héritent de java.util.AbstractList et AbstractCollection
      • Implémentent les interfaces Collection et List
    • Listes chaînées : LinkedList
  • Interface Set
    • Tables de hachage : HashSet
    • Arbres à balance équilibrée : TreeSet
  • Interface Map
    • Tables de hachage : HashMap
    • Arbres à balance équilibrée : TreeMap

Un premier exemple

Le programme suivant ajoute trois chaînes de caractères (“Medor”, “Rex” et “Brutus”) à la liste maListe et l’affiche.

import java.util.*; // Importation de toutes les classes du paquetage java.util
List maListe = new ArrayList(); // Création d'une nouvelle liste de type ArrayList
maListe.add("Medor"); // Ajout de l'élément "Medor" à la liste
maListe.add("Rex"); // Ajout de l'élément "Rex" à la liste
maListe.add("Brutus"); // Ajout de l'élément "Brutus" à la liste

System.out.println(maListe); // Affichage du contenu de la liste
[Medor, Rex, Brutus]

Administration des collections

  • Tri, recherche, min, max : Méthodes statiques pour gérer les collections.
  • Classe Collections :
    • Algorithmes polymorphes pour manipuler les collections.
    • Wrappers pour créer de nouvelles collections basées sur des collections existantes.
    • Exemple : Collections.sort(maListe); pour trier une liste.
  • Classe Arrays :
    • Méthodes pour manipuler les tableaux (tri, recherche).
    • Fabrique statique pour voir les tableaux comme des listes.
    • Exemple : Arrays.sort(monTableau); pour trier un tableau.
  • Classe Arrays :
    • Méthodes pour manipuler les tableaux (tri, recherche).
    • Fabrique statique pour voir les tableaux comme des listes.
    • Exemple : Arrays.sort(monTableau); pour trier un tableau.

Les standards et les exceptions

  • Constructeurs sans paramètres et avec Collection : Disponibles pour la plupart des classes de collections.
  • Factories : Disponibles via Arrays et des méthodes statiques des interfaces (depuis Java 9).
  • Garantie limitée : Les interfaces ne peuvent pas garantir la présence de constructeurs.
  • Conversions facilitées : Simplifient les conversions entre différentes implémentations de collections.
  • Implantation complète : Toutes les méthodes de l’interface doivent être implémentées.
  • Méthodes non pertinentes : Certaines méthodes n’ont pas de sens pour certaines spécialisations.
    • Exemple : Collections en lecture seule (méthodes add(), put(), etc.) retournent une exception UnsupportedOperationException.
// Création d'une liste à partir d'un tableau
List list1 = Arrays.asList("Pierre", "Paul","Paul");
// Création d'un ensemble immuable
Set set1 = Set.of("Marie","Denise","Victoire");

// Affichage des collections
System.out.println("List: "+list1);
System.out.println("Set: "+set1);
List: [Pierre, Paul, Paul]
Set: [Victoire, Marie, Denise]

Des collections “génériques” ?

  • Les collections représentent des ensembles d’objets (cf. type de retour de l’interface).
  • Un objet ou un ensemble d’objets extraits sont donc du type Object.
  • La conséquence est donc que les objets extraits doivent être transtypés.
  • De plus les fonctions prennent en paramètres des instances de Object, il est donc difficile de contrôler la consistance.

Un exemple de transtypage 1/2

// Classe abstraite représentant un animal
public abstract class Animal { 
    // Méthode abstraite que chaque animal doit implémenter pour définir son cri
    public abstract String crier(); 
}

public class Chien extends Animal {
    // Implémentation de la méthode crier pour les chiens
    @Override
    public String crier() {
        return System.identityHashCode(this) + " aboie.";
    }

    // Méthode spécifique aux chiens
    public void sePromenerEnLaisse() {
        // Logique pour promener le chien en laisse
    }
}

public class Chat extends Animal {
    // Implémentation de la méthode crier pour les chats
    @Override
    public String crier() {
        return System.identityHashCode(this) + " miaule.";
    }
}

Un exemple de transtypage 2/2

import java.util.*;
List l = new ArrayList();
l.add(new Chien());
l.add(new Chien());
l.add(new Chat());
System.out.println("Taille de la liste: "+l.size());
Taille de la liste: 3
l.get(0).crier();
CompilationException: 
l.get(0).crier();
cannot find symbol
  symbol:   method crier()
((Animal)l.get(2)).crier();
307653971 miaule.
((Chien)l.get(2)).sePromenerEnLaisse();
EvalException: class REPL.$JShell$71$Chat cannot be cast to class REPL.$JShell$70$Chien (REPL.$JShell$71$Chat and REPL.$JShell$70$Chien are in unnamed module of loader jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader @706a04ae)
Execution exception
---------------------------------------------------------------------------

java.lang.ClassCastException: class REPL.$JShell$71$Chat cannot be cast to class REPL.$JShell$70$Chien (REPL.$JShell$71$Chat and REPL.$JShell$70$Chien are in unnamed module of loader jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader @706a04ae)
    at .(#102:5)

Les types génériques (1/2)

  • Définition : Un type générique (ou type paramétré) permet de définir des classes, interfaces et méthodes avec des types variables.
  • Avantages :
    • Réutilisabilité du code
    • Sécurité de type à la compilation
    • Élimination des conversions explicites

Les types génériques (2/2)

public class Couple<T1, T2> {
    private final T1 e1;
    private final T2 e2;

    public Couple(T1 p1, T2 p2) {
        this.e1 = p1;
        this.e2 = p2;
    }

    public T1 getFirst() {
        return e1;
    }

    public T2 getSecond() {
        return e2;
    }
}

// Exemple d'utilisation
Couple<Chien, Chat> c = new Couple<>(new Chien(), new Chat());

c.getFirst().crier();
2091993409 aboie.

Les types bornés (bounded types)

  • Définition : Les types bornés permettent de restreindre les types génériques à des sous-types spécifiques.
  • Syntaxe : <T extends SuperType>

Syntaxe

La syntaxe pour définir un type borné est <T extends SuperType>. Par exemple, si vous voulez créer une méthode générique qui accepte uniquement des instances de Number ou de ses sous-classes, vous pouvez utiliser la syntaxe suivante :

Les wildcards (1/2)

  • Définition : Les wildcards (?) permettent de travailler avec des types inconnus.
  • Types de wildcards :
    • ? extends T : Borne supérieure
    • ? super T : Borne inférieure

Les wildcards (2/2)

Voici un exemple d’utilisation de wildcard avec une borne supérieure :

Et un exemple avec une borne inférieure :

Les collections sont génériques

List<Chien> listeDeChiens = new ArrayList<>();
listeDeChiens.add(new Chien()); 
listeDeChiens.add(new Chien());
for(Chien chien:listeDeChiens)
    System.out.println(chien.crier());
196339111 aboie.
91495073 aboie.
listeDeChiens.add(new Chat());
CompilationException: 
listeDeChiens.add(new Chat());
incompatible types: Chat cannot be converted to Chien

Une liste polymorphe:

List<Animal> animaux = new ArrayList(listeDeChiens);
animaux.add(new Chat());
System.out.println("Taille de la liste d'animaux: "+animaux.size());
Taille de la liste d'animaux: 3
for (Animal a:animaux)
    System.out.println(a.crier());
196339111 aboie.
91495073 aboie.
21728983 miaule.

Une liste d’entiers:

List<Integer> l = new ArrayList<>();
l.add(3); // Utilisation de l'autoboxing
l.add(4);

int sum = l.get(0) + l.get(1);
System.out.println("La somme est " + sum);
La somme est 7

Le parcours d’une collection (1/2)

  • Boucles traditionnelles :
    • Les collections indexées (Listes, etc.) peuvent être parcourues avec une boucle.
    • Limitation : Cette méthode n’est pas idéale pour l’évolutivité car elle supprime l’encapsulation des collections.
  • Itérateur :
    • Une instance de l’interface Iterator permettant d’énumérer les éléments d’une collection.
    • Toutes les collections possèdent la méthode iterator() qui retourne un itérateur.
    • Les itérateurs peuvent être spécialisés en fonction des sous-classes de Collection (ex : ListIterator).
  • Avantages des Itérateurs
    • Encapsulation :
      • Maintient l’encapsulation des collections.
    • Modification :
      • Permet de modifier la collection en cours de parcours.

Le parcours d’une collection - Exemple (2/2)

for (int i=0;i<animaux.size();i++)
  System.out.println(animaux.get(i).crier()) ;
196339111 aboie.
91495073 aboie.
21728983 miaule.
Iterator<Animal> itAnimaux = animaux.iterator();
while (itAnimaux.hasNext())
  System.out.println(itAnimaux.next()
                     .crier().toUpperCase());
196339111 ABOIE.
91495073 ABOIE.
21728983 MIAULE.

La conversion vers un tableau

  • Méthode toArray() :
    • Attention au type de retour : La méthode toArray() retourne un tableau d’objets.
    • Passage d’un tableau en paramètre : Il est possible de passer un tableau en paramètre à toArray(T[] a).
    • Pourquoi ne pas simplement transtyper le résultat ? : Le transtypage peut entraîner des erreurs de type à l’exécution.
    • Taille du tableau : Si le tableau passé en paramètre est trop petit, un nouveau tableau du même type est créé.
    • Collection vide : Une collection vide retourne un tableau vide (et non null).
Object[] tObjets = listeDeChiens.toArray();
System.out.println(Arrays.toString(tObjets));
for (Object c:tObjets) {System.out.println(((Chien)c).crier());};
[REPL.$JShell$70$Chien@bb3e5a7, REPL.$JShell$70$Chien@5741aa1]
196339111 aboie.
91495073 aboie.

Conversion correcte

Chien[] desChiens = new Chien[listeDeChiens.size()];
listeDeChiens.toArray(desChiens);
for (Chien c:desChiens) {System.out.println(c.crier());};
196339111 aboie.
91495073 aboie.

Trier une collection

  • Les classes Collections et Arrays proposent des méthodes de classe pour traiter les collections et des tableaux.
  • Elles permettent en particulier de trier et de rechercher dans des listes, de faire des copies, …
  • Nous avons déjà vu que l’on pouvait trier une collection de String, en réalité pour qu’une collection puisse être triée, ses élements doivent implanter l’interface Comparable :
    • int compareTo(T o) Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
  • Attention à la consistance entre Comparable() et equals().

Egalité et Comparaison

  • Interface Comparable :
    • Définit l’ordre naturel des objets.
    • Méthode : int compareTo(T o)
  • Interface Comparator :
    • Permet des méthodes de comparaison personnalisées.
    • Classe : ComparateurPoidsAnimal
    • Méthode : int compare(T o1, T o2)

Exemple

public class Animal implements Comparable<Animal> {
  public final String nom ;
  public final int age ;
  public final int poids ;
  public Animal(String nom) {this(nom, -1, -1);}
  public Animal(String nom, int age, int poids)
    {this.nom=nom; this.age=age; this.poids=poids;}  
  public int compareTo (Animal a) {
    return nom.compareTo(a.nom);
  }
  public boolean equals (Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Animal animal = (Animal) o;
    //return nom.equals(animal.nom);
    return age == animal.age && poids == animal.poids && nom.equals(animal.nom);    
  }
  public String toString ( ) {
    return "Animal(nom="+nom+",age="+age+",poids="+poids+")";}
}

Utilisation

List<Animal> l = new ArrayList<Animal >() ;
Animal figaro=new Animal("Figaro", 3, 2 );
//Nom, age , poids
l.add(new Animal("Medor", 2, 5 ));
l.add(figaro);
l.add(new Animal("Brutus", 1, 15));
System.out.println(l);

Collections.sort(l); // Trier par nom
System.out.println(l);
[Animal(nom=Medor,age=2,poids=5), Animal(nom=Figaro,age=3,poids=2), Animal(nom=Brutus,age=1,poids=15)]
[Animal(nom=Brutus,age=1,poids=15), Animal(nom=Figaro,age=3,poids=2), Animal(nom=Medor,age=2,poids=5)]
public class ComparateurPoidsAnimal implements Comparator<Animal> {
  public int compare (Animal animal1 , Animal animal2) {
    return animal1.poids-animal2.poids;
  }
}
// Trier par poids
Collections.sort (l , new ComparateurPoidsAnimal());
System.out.println(l);
[Animal(nom=Figaro,age=3,poids=2), Animal(nom=Medor,age=2,poids=5), Animal(nom=Brutus,age=1,poids=15)]

Recherche dans une collection

Collections.sort(l);
System.out.println(l);
int positionFigaroNom=Collections.binarySearch(l,figaro);
System.out.println("Figaro position par nom: "+positionFigaroNom);
[Animal(nom=Brutus,age=1,poids=15), Animal(nom=Figaro,age=3,poids=2), Animal(nom=Medor,age=2,poids=5)]
Figaro position par nom: 1
Collections.sort (l , new ComparateurPoidsAnimal());
System.out.println(l);
int positionFigaroPoids = Collections.binarySearch(l,figaro,new ComparateurPoidsAnimal());
System.out.println("Figaro position par poids: "+positionFigaroPoids);
[Animal(nom=Figaro,age=3,poids=2), Animal(nom=Medor,age=2,poids=5), Animal(nom=Brutus,age=1,poids=15)]
Figaro position par poids: 0

Utilisation des Set avec des arbres (TreeSet)

  • Définition : TreeSet est une implémentation de l’interface Set basée sur un arbre rouge-noir.
  • Caractéristiques :
    • Maintient les éléments dans un ordre trié.
    • Offre des performances logarithmiques pour les opérations de base (insertion, suppression, recherche).
  • Avantages :
    • Maintien de l’ordre naturel des éléments.
    • Recherche rapide des éléments.

Utilisation des Set avec des tables de hachage (HashSet)

  • Définition : HashSet est une implémentation de l’interface Set basée sur une table de hachage.
  • Caractéristiques :
    • Ne maintient pas l’ordre des éléments.
    • Offre des performances moyennes constantes pour les opérations de base (insertion, suppression, recherche).
  • Avantages :
    • Performances rapides pour les opérations de base.
    • Idéal pour les ensembles où l’ordre n’est pas important.

Set hashSet = new HashSet<>(); hashSet.add(“Banane”); hashSet.add(“Pomme”); hashSet.add(“Orange”); System.out.println(hashSet); // Affiche les éléments sans ordre particulier

Redéfinition de hashCode() et equals()

  • Importance : Pour utiliser une classe dans une table de hachage (HashSet, HashMap)
  • Règles :
    • Si deux objets sont égaux selon equals(), ils doivent avoir le même hashCode
    • Si deux objets ont le même hashCode, ils ne sont pas nécessairement égaux
    • hashCode() doit retourner la même valeur pour le même objet

Redéfinition de hashCode() et equals()

L’utilisation des Maps

  • Une Map associe une clé unique à une valeur spécifique.
  • Chaque clé est unique (equals() retourne false pour deux clés différentes).
  • Pour les tables de hachage :
    • Utilisation de hashCode() pour générer un code de hachage.
    • Distribution uniforme des entrées pour améliorer les performances de recherche.
  • Implémentation avec des arbres :
    • Arbres binaires de recherche pour maintenir les clés dans un ordre trié.
    • Opérations de recherche, d’insertion et de suppression en temps logarithmique.
  • Les clés doivent implémenter l’interface Comparable pour définir un ordre naturel.
  • Utilisations courantes des Maps :
    • Ajouter et supprimer des entrées.
    • Rechercher un objet par sa clé.
    • Récupérer les clés associées à une valeur.
  • Les Maps sont essentielles pour organiser et accéder efficacement aux données.
  • En Java, il s’agit d’une interface implémentée notamment par :
    • HashMap : Une table de hachage offrant un accès en \(\mathcal{O}(1)\).
    • TreeMap : Un arbre équilibré qui ordonne les valeurs en fonction des clés :
      • L’accès est en \(\mathcal{O}(\log n)\) (où \(n\) est le nombre d’éléments).
      • Les objets doivent implémenter l’interface Comparable.
      • Il est également possible de fournir un comparateur externe (instance de Comparator).

L’interface Map<K,V>

void clear()
boolean containsKey(Object key)
boolean containsValue(Object value)
Set<Map.Entry<K,V>> entrySet()
boolean equals(Object o)
V get(Object key)
int hashCode()
boolean isEmpty()
Set<K> keySet()
V put(K key, V value)
void putAll(Map<? extends K,? extends V> t)
V remove(Object key)
int size()
Collection<V> values()

Fonctionnement de Map

  • Par défaut, une Map stocke des instances de Object.
  • L’utilisation des génériques est possible et fortement recommandée.
  • Les couples clé/valeur sont manipulés via l’interface Map.Entry.
    • Trois méthodes principales : getKey(), getValue(), setValue().
  • La méthode entrySet() de Map retourne un ensemble (Set) de Map.Entry.
  • Pour garantir le bon fonctionnement, une clé ne peut être remplacée que par une clé égale (cf. equals()).
  • Conseil : effectuer une opération d’ajout suivie d’une suppression pour tester le comportement.

Exemple de Map

public final class Chien {
 public final String nom;
 public Chien(String nom) {this.nom = nom;}
 public String toString() {return "Chien(){nom="+nom+"})";}
}

Map<String,Chien> map1 = new HashMap<String,Chien>();
map1.put("Ch4", new Chien( "First"));
map1.put("Ch3", new Chien( "Medor"));
map1.put("Ch1", new Chien( "Rex"));
map1.put("Ch2", new Chien( "Medor"));
map1.put("Ch2", new Chien( "Brutus"));
System.out.println("HashMap: "+map1);

Utilisation

System.out.println ("Le chien Ch2 est "+map1.get("Ch2").nom);

Map<String,Chien> map2 = new TreeMap<String,Chien>(map1);
System.out.println("TreeMap: "+map2);

SortedMap<String,Chien> map3 = new TreeMap<String,Chien>(map1);
System.out.println("Sub SortedMap: "+map3.subMap("Ch2","Ch3"+"\0"));

Parcourir une Map

  • Pour parcourir une Map :
    • Récupérer l’ensemble des clés avec keySet().
    • Parcourir cet ensemble à l’aide d’un itérateur ou d’une boucle for each.
    • Pour chaque entrée, récupérer la clé avec getKey() et la valeur avec getValue().
    • La méthode values() retourne la collection des valeurs correspondantes.
Set<Map.Entry<String,Chien>> setChiens = map1.entrySet();

for (Map.Entry<String,Chien> uneEntree : setChiens)
    System.out.print(uneEntree.getKey()+ " :" +uneEntree.getValue());

Les critères de choix de classe

  • Un des objectifs des programmes Java est la réutilisabilité.
  • Il est crucial de choisir la classe ou l’interface la plus adaptée.
  • Le programme doit être utilisable dans un maximum de cas possibles.
  • Pour cela, il est recommandé de :
    1. Manipuler les objets via des interfaces aussi générales que possible pour les paramètres.
    2. Choisir des types de retour pour les objets instances des classes les plus spécifiques possibles, offrant ainsi le plus de fonctionnalités.
    3. Toutefois, ce dernier choix peut poser des problèmes en cas de changement d’implémentation ; il est donc préférable d’opter également pour des interfaces plus générales.

Arguments variables

  • Varargs en Java :
    • Permettent de passer un nombre variable d’arguments du même type à une méthode.
    • Simplifient le passage d’arguments sans créer explicitement un tableau ou une collection.
    • Utilisation des varargs avec l’ellipse (...) après le type de l’argument.
    • Flexibilité et praticité : la méthode peut être appelée avec n’importe quel nombre d’arguments de type int.
public static int somme (int... entiers) {
  int total = 0 ;
  for (int e:entiers) total+=e;
  return total ;
}
System.out.println("1+2 = "+somme(1,2));
System.out.println("1+2+3+4 = "+somme (1,2,3,4));

Implémentations Spécialisées de Set et Map pour les Enums

  • EnumSet
    • Implémentation de Set haute performance basée sur un bit-vecteur.
    • Tous les éléments d’une instance de EnumSet doivent appartenir à un seul type d’énumération.
  • EnumMap
    • Implémentation de Map haute performance basée sur un tableau.
    • Toutes les clés d’une instance de EnumMap doivent appartenir à un seul type d’énumération.

Exemple

public class Person {
    public enum PetType {CAT, DOG};
    private String name;
    private EnumSet<PetType> pets;
    public Person(String name, Set<PetType> petsTypes) {this.name=name; this.pets=EnumSet.copyOf(petsTypes);}
    public boolean hasPet(PetType petType) {return pets.contains(petType);}
    public String toString() {return name;}
}

Exemple

public enum Jour {
  LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI, DIMANCHE;
}

EnumMap<Jour, Animal> animalParJour = new EnumMap<Jour, Animal>(Jour.class);

animalParJour.put(Jour.MARDI,new Animal("Rex"));
animalParJour.put(Jour.VENDREDI,new Animal("Médor"));
System.out.println(animalParJour);
System.out.println(animalParJour.get(Jour.VENDREDI));

Eclipse Collections

  • Une alternative externe au JDK, offrant une bibliothèque beaucoup plus complète :
    • Eclipse Collections: https://eclipse.dev/collections/
  • Propose de nombreuses structures de données supplémentaires, à la fois mutables et immutables.
  • Intègre pleinement les expressions lambda et les streams pour une programmation fonctionnelle efficace.

Exemple

%maven org.eclipse.collections:eclipse-collections-api:11.1.0
%maven org.eclipse.collections:eclipse-collections:11.1.0
MutableIntList intList = IntLists.mutable.of(1, 2, 3);
intList;

Exemple

MutableList<Person> mutableListWith =
  Lists.mutable.with(
    new Person("P1",Set.of(Person.PetType.CAT)),
    new Person("P2",Set.of(Person.PetType.DOG)),
    new Person("P3",Set.of(Person.PetType.CAT, Person.PetType.DOG)));

List<Person> peopleWithCats =
  mutableListWith
    .stream()
    .filter(person -> person.hasPet(Person.PetType.CAT))
    .collect(Collectors.toList());

System.out.println(peopleWithCats);