Vous êtes sur la page 1sur 29

PROGRAMMATION

JAVA
Deuxième année Informatique (ENICARTHAGE)

1
LES COLLECTIONS ET
LES STREAMS
2
GÉNÉRICITÉ
❖ En POO, la généricité est un concept permettant de définir des algorithmes (types de
données et méthodes) identiques qui peuvent être utilisés sur de multiples types de
données. Cela permet donc de réduire les quantités de codes à produire.
// exemple 1: ArrayList
import java.util.ArrayList;
import java.util.Date; Essai
public class Genericite { Cours Java
public static void main(String[] args) { Tutoriel Java
// création d'un tableau dynamique des instances de Object Thu Oct 06 14:10:04 GMT+01:00 2022
ArrayList collection = new ArrayList();
// On y stocke nos chaînes de caractères (possible par polymorphisme)
collection.add( "Essai" ); collection.add( "Cours Java" ); collection.add( "Tutoriel Java" );
// Mais rien nous interdit d'y mettre autre chose (polymorphisme)
collection.add( new Date() );
// Maintenant on parcourt la collection
for ( Object value : collection ) {
3
System.out.println( value ); }}}
// Si nous utilisons la généricité, nous pouvons indiquer que la collection ne peut contenir que des chaînes de caractères
ArrayList<String> collection = new ArrayList<String>();

// On y stocke nos chaînes de caractères


collection.add( "Essai" ); collection.add( "Cours Java" ); collection.add( "Tutoriel Java" );

// Cette ligne ne compile plus


collection.add( new Date() );
// exemple 2: création d’une classe générique
public class GenericBox<T>{ public class ClassesGeneriques { enicar
private T element; 123
public GenericBox( T element ) { public static void main(String[] args) {
this.element = element; }
public T getElement() { GenericBox<String> box = new GenericBox<>( "enicar" );
return element; } System.out.println( box.getElement() );
public void setElement( T element ) { GenericBox<Integer> box1 = new GenericBox<>(123 );
this.element = element; } } System.out.println( box1.getElement() ); }}

❖ classe générique avec plusieurs types de généricité: public class ………<T1,T2>


❖ Restriction de typage sur une classe générique; par exemple:
public class GenericBox<T extends Personne>
// juste les types dérivant de la classe Personne 4
LES COLLECTIONS
❖ Une collection est une structure de données en mémoire, qui contient toutes les valeurs
que la structure de données possède actuellement. Des opérations telles que la
recherche, le tri, l'insertion, la manipulation et la suppression peuvent être effectuées sur
une collection.
❖ Le Java SE propose un très grand nombre de classes génériques dans sa librairie et
notamment les classes de collections. Elles implémentent des interfaces communes qui
sont elles-mêmes génériques:
public interface Collection<E> extends Iterable<E>
public interface List<E> extends Collection<E>
public interface Set<E> extends Collection<E>
public interface Map<Key, Value>
5
Tableau de Liste chainée Table avec hashage Arbre
taille variable
Interfaces Classes implémentant ces interfaces
List ArrayList LinkedList<T
<T> >
Set HashSet<T> TreeSet<T>

Map HashMap<T1,T2> TreeMap<T1,T2>


❖ Quelques méthodes de Collection:
✔ int size(); ✔ boolean containsAll(Collection c);
✔ boolean isEmpty(); ✔ boolean addAll(Collection c);
✔ boolean contains(Object element); ✔ boolean removeAll(Collection c);
✔ boolean add(Object element);
✔ void clear();
✔ boolean remove(Object element);
✔ Object[] toArray();
✔ int hashCode();
✔ boolean equals(Object element);
6
❖ Quelques méthodes de List: ❖ Quelques méthodes de Map:
✔ Object get(int index); ✔ int size();
✔ Object set(int index, Object element); ✔ Object put(Object key, Object value);

✔ void add(int index, Object element); ✔ Object get(Object key);


✔ Object remove(Object key);
✔ Object remove(int index);
✔ boolean containsKey(Object key);
✔ boolean addAll(int index, Collection c);
✔ boolean containsValue(Object value);
✔ int indexOf(Object o);
✔ boolean isEmpty();
✔ int lastIndexOf(Object o);
✔ void putAll(Map t);
✔ List subList(int fromIndex, int toIndex);
✔ void clear();
✔ public Set keySet();
✔public Collection values();

7
// exemple:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public enum Category { Ouvrier, Ingenieur, Directeur}
public class Employe {
private int id; private String name; private int age; private double salary; private Category cat;
public Employe(int id, String name, int age, double salary, Category cat) {
this.id = id; this.name = name; this.age = age; this.salary = salary; this.cat = cat; }
public int getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; }
public double getSalary() { return salary; }
public Category getCategory(){ return cat; }
public void setId(int id) { this.id = id; }
public void setName(String name) { this.name= name; }
public void setAge(int age) { this.age = age; }
public void setSalary(double salary ) { this.salary=salary; }
public void setCategory(Category cat) { this.cat=cat; }
8
@Override
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" + this.salary + " Category: "+this.cat+ "]";
}}
public class LesCollections {
public static void main(String[] args) {
// tableaux statiques collections
System.out.println("---------------- Array -------------------");
Employe[] employes1 = {new Employe(0,"Ferid", 25, 2000.0, Category.Ingenieur), new Employe(1,"Samia", 30, 3000.1,
Category.Directeur)};
Employe[] employes2 =new Employe[2];
employes2[0] = new Employe(0,"Ferid", 25, 2000.0, Category.Ingenieur);
employes2[1] = new Employe(1,"Samia", 30, 3000.1, Category.Directeur);
System.out.println("affichage du contenu tableau statique");
for (Employe emp:employes2){
System.out.println(emp); }
// tableaux dynamiques
System.out.println("---------------- ArrayList-------------------");
List<Employe> employeList = new ArrayList<Employe>();
employeList.add(new Employe(0,"Ferid", 25, 2000.0, Category.Ingenieur));
employeList.add(0, new Employe(1,"Samia", 30, 3000.1, Category.Directeur));
System.out.println("affichage du contenu ArrayList");
for (Employe emp:employeList){
System.out.println(emp); } 9
// listes chainées
System.out.println("---------------- LinkedList -------------------");
List<Employe> employeList1 = new LinkedList<Employe>();
employeList1.add(new Employe(0,"Ferid", 25, 2000.0, Category.Ingenieur));
employeList1.set(0, new Employe(1,"Samia", 30, 3000.1, Category.Directeur));
System.out.println("affichage du contenu LinkedList");
for (Employe emp:employeList1){ System.out.println(emp); }
// Emsembles
System.out.println("---------------- Set -------------------");
Set<String> se = new HashSet<String>() ;
se.add("un"); se.add("deux") ; se.add("un") ;
System.out.println("Taille du set : " + se.size()) ;
System.out.println("affichage du contenu Set");
for (String emp:se){ System.out.println(emp); }
// tables associatives
System.out.println("---------------- Map-------------------");
Map <String,Employe> m = new HashMap(); int i=0;
m.put("c", new Employe(i,"Salah", 30, 600.25, Category.Ouvrier));
m.put("f", new Employe(i++,"Said", 27, 600.25, Category.Ouvrier));
m.put("k", new Employe(i++,"Fethi", 32, 600.25, Category.Ouvrier));
m.put("P", new Employe(i++,"Nabiha",29 , 1500.0, Category.Ingenieur));
System.out.println("valeur associée à f est: "+m.get("f"));
System.out.println("liste des valeurs "+ m.values());
System.out.println("liste des clés: "+m.keySet());}} 10
---------------- Array -------------------
affichage du contenu tableau statique
[id=0, name=Ferid, age=25, salary=2000.0 Category: Ingenieur]
[id=1, name=Samia, age=30, salary=3000.1 Category: Directeur]
---------------- ArrayList-------------------
affichage du contenu ArrayList
[id=1, name=Samia, age=30, salary=3000.1 Category: Directeur]
[id=0, name=Ferid, age=25, salary=2000.0 Category: Ingenieur]
---------------- LinkedList -------------------
affichage du contenu LinkedList
[id=1, name=Samia, age=30, salary=3000.1 Category: Directeur]
---------------- Set -------------------
Taille du set : 2
affichage du contenu Set
un
deux
---------------- Map-------------------
valeur associée à f est: [id=0, name=Said, age=27, salary=600.25 Category: Ouvrier]
liste des valeurs [[id=2, name=Nabiha, age=29, salary=1500.0 Category: Ingenieur], [id=0, name=Salah, age=30,
salary=600.25 Category: Ouvrier], [id=0, name=Said, age=27, salary=600.25 Category: Ouvrier], [id=1,
name=Fethi, age=32, salary=600.25 Category: Ouvrier]]
liste des clés: [P, c, f, k]
11
Il existe une autre façon pour créer des listes en utilisant la méthode statique asList() de la
classe Arrays. Rappelons que cette classe comporte un ensemble de méthodes statiques qui
s’appliquent sur des tableaux.
// continuons avec le même exemple:
// autre façon de créer des listes avec méthodes statique de Arrays
System.out.println("---------------- liste avec Arrays -------------------");
List<Employe> employes3 = Arrays.asList(new Employe(i,"Salah", 30, 600.25, Category.Ouvrier),new
Employe(i++,"Said", 27, 600.25, Category.Ouvrier));
for (Employe emp:employes3){
System.out.println(emp); }

---------------- liste avec Arrays -------------------


[id=3, name=Salah, age=30, salary=600.25 Category: Ouvrier]
[id=3, name=Said, age=27, salary=600.25 Category: Ouvrier]

12
CLASSE COLLECTIONS
Classe identique à la classe Arrays avec des méthodes statiques qui s’applique sur des
collections:
Tri: sort(List list) ; sort(List list,Comparator comp)
Mélange aléatoire: shuffle(List liste) ;
Manipulations: reverse(List liste) ; fill (List liste, Object element) ; copy(List dest, List src) ;
Recherche binaire: binarySearch(List list, Object element) ;
// continuons avec le même exemple:
// test des méthodes statiques de Collections
System.out.println("---------------- classe Collections -------------------");
List<Integer> entiers = Arrays.asList(12,45,10,-5,56);
yetbadlou fil list entiers , tsirlou tri
Collections.sort(entiers);

13
for (Integer emp:entiers){
System.out.print(emp+" "); }
System.out.println();
System.out.println(Collections.binarySearch(entiers, 45));
System.out.println(Collections.binarySearch(entiers, 6));
List<String> chaines = Arrays.asList("Ali","Enicar","Car","Bac");
Collections.sort(chaines);
for (String emp:chaines){
---------------- classe Collections -------------------
System.out.print(emp+" "); }
-5 10 12 45 56
System.out.println();
3
System.out.println(Collections.max(chaines));
-2
}
Ali Bac Car Enicar
Enicar

// Comment faire le tri/chercher les bornes (…) d’une collection des employés?

14
LES INTERFACES DE
COMPARAISON
❖ Java propose l’interface générique Comparable <T> qui doit être implémentée par une classe
si nous voulons utiliser les méthodes de tri d’Arrays ou Collections. Elle possède une seule
méthode int compareTo(T obj)
✔ retourne un entier négatif si l’objet qui fait l’appel est plus petit que obj
✔ nul (égal) si ils sont identiques,
✔ ou a positif (supérieur) si il est plus grand
❖ L’interface générique Comparator <T> propose une méthode int compare(Object o1, Object
o2). Celle-ci retourne un entier négatif, nul ou positif si le premier objet est respectivement
inférieur, égal ou supérieur au deuxième

15
// Modifions le même exemple:
public class Employe implements Comparable<Employe> { // mêmes attributs et méthodes
@Override
public int compareTo(Employe o) {
return (this.age-o.age); } }
// dans la classe principale
List<Employe> employes4 = Arrays.asList(new Employe(0,"Salah", 30, 600.25,
Category.Ouvrier),new Employe(1,"Said", 27, 600.25, Category.Ouvrier));
System.out.println("-----------tri avec Comparable -------");
System.out.println("liste avant le tri");
for (Employe emp:employes4){ -----------tri avec Comparable -------
System.out.println(emp); } liste avant le tri
Collections.sort(employes4); [id=0, name=Salah, age=30, salary=600.25 Category: Ouvrier]
System.out.println("liste après le tri"); [id=1, name=Said, age=27, salary=600.25 Category: Ouvrier]
for (Employe emp:employes4){ liste après le tri
System.out.println(emp); } [id=1, name=Said, age=27, salary=600.25 Category: Ouvrier]
[id=0, name=Salah, age=30, salary=600.25 Category: Ouvrier]

16
// Modifions le même exemple: avec l’interface fonctionnelle Comparator
// dans la classe principale
List<Employe> employes5 = Arrays.asList(new Employe(0,"Salah", 30, 600.25, Category.Ouvrier),new Employe(1,"Said",
27, 1000.25, Category.Ouvrier));
System.out.println("-----------tri avec Comparator -------");
System.out.println("liste avant le tri");
for (Employe emp:employes5){
System.out.println(emp); }
Collections.sort(employes5, (o1,o2)->o1.getAge()-o2.getAge());
System.out.println("liste après le tri d'age");
for (Employe emp:employes5){
System.out.println(emp); }
Collections.sort(employes5, (o1,o2)->(int)(o1.getSalary()-o2.getSalary()));
System.out.println("liste après le tri de salaire");
for (Employe emp:employes5){ -----------tri avec Comparator -------
System.out.println(emp); } liste avant le tri
[id=0, name=Salah, age=30, salary=600.25 Category: Ouvrier]
[id=1, name=Said, age=27, salary=1000.25 Category: Ouvrier]
liste après le tri d'age
[id=1, name=Said, age=27, salary=1000.25 Category: Ouvrier]
[id=0, name=Salah, age=30, salary=600.25 Category: Ouvrier]
liste après le tri de salaire
[id=0, name=Salah, age=30, salary=600.25 Category: Ouvrier]
[id=1, name=Said, age=27, salary=1000.25 Category: Ouvrier]
17
AUTRES INTERFACES
FONCTIONNELLES GÉNÉRIQUES
❖ public interface Consumer <T> { UTILES
void accept(T); }
❖ Parmi les méthodes des collections d’un type T: forEach (Consumer<? Super T>)
// exemple d’utilisation
List <Employe> employes5 = Arrays.asList(new Employe(0,"Salah", 30, 600.25, Category.Ouvrier),new
Employe(1,"Said", 27, 1000.25, Category.Ouvrier));
// au lien de: for (Employe emp:employes5) { System.out.println(emp); }
// en utilisant les expressions Lambda
employes5.forEach((Employe e) -> System.out.println(e));
// en utilisant reference au méthode:
employes5.forEach(System.out::println);

18
❖ public interface Supplier <T> { T get(); }
❖ public interface Predicate <T> { boolean test(T); }
❖ public interface Function <T1, T2> { T2 apply(T1); }
// exemple d’utilisation
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class InterfacesUtiles { 120.0
Suceess
public static void main(String[] args) {
true
Function <Integer,Double> fun = i->i*10.0; System.out.println(fun.apply(12));
Supplier <String> supp = ()-> "Suceess"; System.out.println(supp.get());
Predicate <Double> pre = n-> n<20; System.out.println(pre.test(15.0));}}

❖ public interface UnaryOperator <T> { T apply(T); }


❖ public interface BiFunction <T1,T2,T3> { T3 apply(T1,T2); }
❖ public interface BinaryOperator <T> { T apply(T,T); } 19
LES STREAMS?
❖ Dans l’objectif des traitements des données (et pas l’enregistrement) Java a introduit les flux ou
streams
❖ Un stream se construit à partir d’une source de données (une collection, un tableau ou des
sources I/O par exemple), et possède un certain nombre de propriétés spécifiques:
✔ Un stream ne stocke pas de données, contrairement à une collection
✔ Un stream ne modifie pas les données de la source sur laquelle il est construit.
✔ Un stream peut ne pas être borné, contrairement aux collections. Il faudra cependant veiller à ce
que nos opérations se terminent en un temps fini
✔ Un stream n’est pas réutilisable. Une fois qu’il a été parcouru, si l’on veut réutiliser les données
de la source sur laquelle il avait été construit, nous serons obligés de reconstruire un nouveau
stream sur cette même source.
20
Avant Java8: effectuer des traitements sur des Collections ou des tableaux en
Java passait essentiellement par l’utilisation du pattern Iterator.
Java 8: propose l’API Stream pour simplifier ces traitements en introduisant un
nouvel objet, Stream.

21
MÉTHODES DES STREAMS
Méthodes de création à partir des collections: stream() et/ou parallel()

22
Méthodes intermédiaires

23
// exemple :
List<Integer> liste = new ArrayList<>();
liste.add(12);liste.add(45);liste.add(-5);liste.add(56); 12
Stream <Integer> str = liste.stream(); 45
Stream <Integer> str1 = str.filter(e->e>0); 56
str1.forEach(System.out::println); cachalot
// ou en une seule ligne chameau
liste.stream().filter(e->e>0).forEach(System.out::println); Chat
3.0
List<String> strings = Arrays.asList("girafe", "chameau", "chat", "poisson", "cachalot"); 9.0
strings.stream().filter(x -> x.contains("cha")).sorted().forEach(System.out::println);
LinkedList <Point> points = new LinkedList();
points.add(new Point(-2,5));
points.add(new Point(7,2));
points.stream().map(p-> p.getX()+p.getY()).forEach(System.out::println);

maptoDouble
Autres formes de map maptoInt
maptoLong
24
System.out.println("tri selon ordonnée croissante"); tri selon ordonnée croissante
points.stream().sorted((p1,p2)->(int)(p1.getY()-p2.getY())).forEach(System.out::println); Point: (7.0,2.0 )
Point: (-2.0,5.0 )
Méthodes terminales:
✔ collect(méthode statique de
public record Commande(String nom, double prix, Client client)
Collectors): {}
public class TerminalesStreams {
La collecte permet de créer un nouvelle collection public static void main(String[] args) {
à partir d’un stream List<Commande> mesCommandes = Arrays.asList(new
// exemple: Commande("Stylos",5.0,new Client(78945689,"Ali")),new
public class Client { Commande("Cartbale",20.5,new Client(78941789,"Samia")), new
private int rip; private String nom; Commande("feuilles",7.2,new Client(78945689,"Ali")));
public Client(int rip, String nom){ List<Client> mesClients = mesCommandes.stream().map( c ->
this.rip = rip; this.nom = nom; } c.client()).collect( Collectors.toList() );
public int getRip(){return rip;} mesClients.forEach(System.out::println);
public String getNom(){return nom;} double chiffreAffaire = mesCommandes.stream().collect(
public void setRip(int rip){this.rip = rip;} Collectors.summingDouble( Commande::prix ) );
public void setNom(String nom){this.nom = nom;} System.out.println(chiffreAffaire); }}
@Override
public String toString(){ Nom du client = Ali de rip: 78945689
return "Nom du client = "+nom+" de rip: "+rip;} Nom du client = Samia de rip: 78941789
@Override Nom du client = Ali de rip: 78945689
public boolean equals(Object o){ 32.7
25
Client cl = (Client)o;
Quelques méthodes statiques de Collectors: toList(), toSet(), summingDouble(),
summingInt(), joining(), groupingBy()…

// continuons l’exemple:
String joined = mesCommandes.stream().map(Commande::toString).collect(Collectors.joining(","));
System.out.println(joined);

Commande[nom=Stylos, prix=5.0, client=Nom du client = Ali de rip: 78945689],Commande[nom=Cartbale, prix=20.5,


client=Nom du client = Samia de rip: 78941789],Commande[nom=feuilles, prix=7.2, client=Nom du client = Ali de rip:
78945689]

Map<String, List<Commande>> map = mesCommandes.stream().collect(Collectors.groupingBy(Commande::nom));


map.keySet().forEach(System.out::println);
map.values().forEach(System.out::println);

Stylos
Cartbale
feuilles
[Commande[nom=Stylos, prix=5.0, client=Nom du client = Ali de rip: 78945689]]
[Commande[nom=Cartbale, prix=20.5, client=Nom du client = Samia de rip: 78941789]]
[Commande[nom=feuilles, prix=7.2, client=Nom du client = Ali de rip: 78945689]]
26
✔ La réduction
La réduction consiste à obtenir un résultat unique à partir d’un stream. Les réductions simples sont celles
auxquelles on pourrait penser en premier lieu : La somme d’éléments (Stream.sum), le maximum
(Stream.max), ou le nombre d’éléments (Stream.count) sont des réductions simples
// continuons l’exemple:
OptionalDouble moy = mesCommandes.stream().mapToDouble(c->c.prix()).average();
System.out.println(moy); OptionalDouble[10.9]
OptionalDouble maximum = mesCommandes.stream().mapToDouble(c->c.prix()).max(); OptionalDouble[20.5]
System.out.println(maximum); OptionalDouble[5.0]
OptionalDouble minimum = mesCommandes.stream().mapToDouble(c->c.prix()).min(); 3
System.out.println(minimum);
Long nbr = mesCommandes.stream().mapToDouble(c->c.prix()).count();
System.out.println(nbr);

L’API streams introduit la notion de Optional. Certaines opérations de réduction peuvent ne


pas être possibles. Par exemple, le calcul de la moyenne n’est pas possible si le stream ne
contient aucun élément. La méthode average qui permet de calculer la moyenne d’un
stream numérique retourne donc un OptionalDouble qui permet de représenter soit le
résultat, soit le fait qu’il n’y a pas de résultat. On peut appeler la méthode
OptionalDouble.isPresent() pour s’assurer qu’il existe un résultat pour cette réduction.
27
AUTRES MÉTHODES DE
CRÉATION DES STREAMS
Stream <Integer> str = Stream.of (3,9,7,5,1);

of Point [] tabp = {new Point(2,1), new Point(7,0)};


Stream <Point> strp = Stream.of(tabp);

IntStream str = IntStream.of (3,9,7,5,1);

iterate iterate(T seed, UnaryOperator<T>)

Stream.iterate(5,i->i*10)

generate generate(Supplier<? extends T>)

Stream.generate(()-> "bonjour "); // Stream infini de String (bonjour)


Stream.generate(Math::random); // Stream infini
Stream.generate(Math::random).limit(8).distinct(); // limiter et sans doublons 28
// spécifique IntStream
IntStream.range(20,23); // suite : 20,21,22
IntStream.rangeClosed(20,23); //suite : 20,21,22,23

// Pour exécuter l’exemple traité sur les streams vous avez besoin des ces importations:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

29

Vous aimerez peut-être aussi