Aller au contenu

Exercices sur les streams

Consignes

  • Ne pas de faire aider par des IA ou genAI

Exercice 1

  • On souhaite modéliser une collection de consoles et jeux rétro. Les consoles (classe VideoGameConsole) et les jeux rétro (classe VideoGame) sont des appareils de divertissement ayant les propriétés: name, releaseYear en commun. Les consoles ont en plus la propriété: companyName (la société qui l'a créé). Les jeux vidéos ont comme propriété supplémentaire l'éditeur du jeu (celui qui le distribue) ainsi que son développeur. On aimerait aussi savoir s'il est indépendant ou pas.
  • Définir les classes nécessaires.
  • Chaque jeu vidéo tient une liste des consoles compatibles (pour les jeux cross-platform) via la propriété: platforms.
  • VideoGameConsole contient en plus la propriété: companyName de type string.
  • Compléter la définition des classes et instancier les jeux et consoles suivantes:
    • Console: name: My first 16 bit Console, releaseYear: 1987, companyName: SEGA
    • Console: name: Another 16 bit console, releaseYear: 1991, companyName: Nintendo
    • Console: name: Awesome 3D console, releaseYear: 1996, companyName: Sony
    • Console: name: 3D console with weird joystick, releaseYear: 1997, companyName: Nintendo
    • Jeu: name: Alex Kidd, releaseYear: 1988, publisher: SEGA
    • Jeu: name: Sonic, releaseYear: 1990, publisher: SEGA
    • Jeu: name: Mario RPG, releaseYear: 1996, publisher: Nintendo
    • Jeu: name: Final Fantasy 6, releaseYear: 1994, publisher: Square
  • En utilisant les streams:
    • Afficher les jeux sortis à partir de l'année 1990 triés par ordre croissant de l'année.
    • Afficher le nom des consoles de la compagnie SEGA.
    • Afficher le nom des consoles dont le nom contient le mot 16 bits.
    • Le nom du premier jeu sorti
    • Trouver le nombre de jeux de chaque éditeur en calculant dictionnaire dont la clé est l'éditeur ou son nom et en valeur le nombre de jeux
    • Pour chaque éditeur, l'année de sortie du premier jeu en calculant dictionnaire dont la clé est l'éditeur ou son nom et en valeur une année
Corrigé
///usr/bin/env jbang "$0" "$@" ; exit $?

//DEPS com.google.guava:guava:33.1.0-jre

import java.util.AbstractMap;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;

import com.google.common.base.Joiner;

abstract class EntertainmentDevice {
  private String name;
  private int releaseYear;

  public EntertainmentDevice(String name, int releaseYear) {
    this.name = name;
    this.releaseYear = releaseYear;
  }

  // propriété calculée
  public boolean isAfter2000() {
    return this.getReleaseYear() >= 2000;
  }

  public boolean isAfter1990() {
    return this.getReleaseYear() >= 1990;
  }

  // proriété avec backieng field (le backing field est name)
  public String getName() {
    return name;
  }

  // proriété avec backieng field (le backing field est name)
  public void setName(String name) {
    this.name = name;
  }

  public int getReleaseYear() {
    return releaseYear;
  }

  public void setReleaseYear(int releaseYear) {
    this.releaseYear = releaseYear;
  }
}

class VideoGameConsole extends EntertainmentDevice {
  private String companyName;

  public VideoGameConsole(String name, int releaseYear, String companyName) {
    super(name, releaseYear);
    this.companyName = companyName;
  }

  public String getCompanyName() {
    return companyName;
  }

  public void setCompanyName(String companyName) {
    this.companyName = companyName;
  }
}

class VideoGame extends EntertainmentDevice {
  private String publisher;

  public VideoGame(String name, int releaseYear, String publisher) {
    super(name, releaseYear);
    this.publisher = publisher;
  }

  public String getPublisher() {
    return publisher;
  }

  public void setPublisher(String publisher) {
    this.publisher = publisher;
  }

  @Override
  public String toString() {
    return "VideoGame [publisher=" + publisher + ", getName()=" + getName() + ", getReleaseYear()=" + getReleaseYear()
        + "]";
  }

}

public class RetroCollectionExercise {

  public static void main(String... args) {
    System.out.println("Retro game collection exercise");
    var consoles = List.of(
        new VideoGameConsole("My first 16 bits Console", 1987, "SEGA"),
        new VideoGameConsole("Another 16 bits console", 1991, "Nintendo"),
        new VideoGameConsole("Awesome 3D console", 1996, "Sony"),
        new VideoGameConsole("3D console with weird joystick", 1997, "Nintendo"));

    var games = List.of(
        new VideoGame("Alex Kidd", 1988, "SEGA"),
        new VideoGame("Starcraft 2", 1988, "BLIZZARD"),
        new VideoGame("Sonic", 1990, "SEGA"),
        new VideoGame("Mario RPG", 1996, "Nintendo"),
        new VideoGame("Final Fantasy 6", 1994, "Square"));

    var gamesAfter1990 = games.stream()
        .filter((game) -> game.getReleaseYear() >= 1990)
        .sorted(Comparator.comparingInt((game) -> game.getReleaseYear()))
        .toList();

    var gamesAfter1990Bis = games.stream()
        .filter(VideoGame::isAfter1990)
        .sorted((g1, g2) -> Integer.valueOf(g1.getReleaseYear()).compareTo(g2.getReleaseYear()))
        .toList();

    System.out.println("\nQ1 - Sort by release year games after 1990");
    gamesAfter1990.stream().forEach((g) -> System.out.println(g));
    System.out.println("Q1 - Bis method" + Joiner.on(",").join(gamesAfter1990Bis));

    var segaConsoleNames = consoles.stream()
        .filter((c) -> c.getCompanyName().equals("SEGA"))
        .map((c) -> c.getName())
        .toList();
    System.out.println("\nQ2 - ");
    segaConsoleNames.stream().forEach((g) -> System.out.println(g));

    var sixteenBitsConsoleNames = consoles.stream()
        .filter((c) -> c.getName().contains("16 bits"))
        .map((c) -> c.getName())
        .toList();

    System.out.println("\nQ3 - 16 bits consoles: ");
    sixteenBitsConsoleNames.stream().forEach((g) -> System.out.println("- " + g));

    System.out.println("\nQ4 - Nom du premier jeu sorti : ");
    Optional<VideoGame> minYearVideoGame = games.stream().min(Comparator.comparingInt((game) -> game.getReleaseYear()));
    if (!minYearVideoGame.isPresent()) {
      return;
    }
    System.out.println(minYearVideoGame.get().getName());

    var minYearVideosGames = games.stream()
        .filter((g) -> g.getReleaseYear() == minYearVideoGame.get().getReleaseYear()).toList();
    System.out.println("\nQ4 ++ - Au cas où il y a plusieurs jeux sortis la même année : ");
    System.out.println(Joiner.on(",").join(minYearVideosGames));

    System.out.println("\nQ5 - Nombre de jeux de chaque éditeur : ");
    var publishers = games.stream().map(g -> g.getPublisher()).distinct().toList();

    var groupedGamesByPublishers = games.stream()
        .collect(Collectors.groupingBy((g) -> g.getPublisher()))
        .entrySet().stream()
        .map((entry) -> new AbstractMap.SimpleEntry<String, Integer>(entry.getKey(), entry.getValue().size())).toList();
    System.out.println(Joiner.on(",").join(groupedGamesByPublishers));

  }
}
///usr/bin/env jbang "$0" "$@" ; exit $?

//DEPS com.google.guava:guava:33.1.0-jre

import java.util.AbstractMap;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;

import com.google.common.base.Joiner;

interface EntertainmentDevice {

  int getReleaseYear();

  // propriété calculée
  default public boolean isAfter2000() {
    return this.getReleaseYear() >= 2000;
  }

  default public boolean isAfter1990() {
    return this.getReleaseYear() >= 1990;
  }
}

record VideoGameConsole(String name, int releaseYear, String companyName) implements EntertainmentDevice {
  @Override
  public int getReleaseYear() {
    return this.releaseYear();
  }
}

record VideoGame(String name, int releaseYear, String publisher) implements EntertainmentDevice {
  @Override
  public int getReleaseYear() {
    return this.releaseYear();
  }
}

public class VideoGameCollectionWithRecords {

  public static void main(String... args) {
    System.out.println("Retro game collection exercise");

    var consoles = List.of(
        new VideoGameConsole("My first 16 bits Console", 1987, "SEGA"),
        new VideoGameConsole("Another 16 bits console", 1991, "Nintendo"),
        new VideoGameConsole("Awesome 3D console", 1996, "Sony"),
        new VideoGameConsole("3D console with weird joystick", 1997, "Nintendo"));

    var games = List.of(
        new VideoGame("Alex Kidd", 1988, "SEGA"),
        new VideoGame("Starcraft 2", 1988, "BLIZZARD"),
        new VideoGame("Sonic", 1990, "SEGA"),
        new VideoGame("Mario RPG", 1996, "Nintendo"),
        new VideoGame("Final Fantasy 6", 1994, "Square"));

    var gamesAfter1990 = games.stream()
        .filter((game) -> game.releaseYear() >= 1990)
        .sorted(Comparator.comparingInt((game) -> game.releaseYear()))
        .toList();

    var gamesAfter1990Bis = games.stream()
        .filter(VideoGame::isAfter1990)
        .sorted((g1, g2) -> Integer.valueOf(g1.releaseYear()).compareTo(g2.releaseYear()))
        .toList();

    var gamesAfter1990Tres = games.stream()
        .filter((game) -> game.releaseYear() >= 1990)
        .sorted((game1, game2) -> {
          if (game1.releaseYear() > game2.releaseYear()) {
            return 1;
          } else if (game1.releaseYear() == game2.releaseYear()) {
            return 0;
          } else {
            return -1;
          }
        })
        .toList();

    System.out.println("\nQ1 - Sort by release year games after 1990");
    gamesAfter1990.stream().forEach((g) -> System.out.println(g));
    System.out.println("Q1 - Bis method" + Joiner.on(",").join(gamesAfter1990Bis));

    var segaConsoleNames = consoles.stream()
        .filter((c) -> c.companyName().equals("SEGA"))
        .map((c) -> c.name())
        .toList();
    System.out.println("\nQ2 - ");
    segaConsoleNames.stream().forEach((g) -> System.out.println(g));

    var sixteenBitsConsoleNames = consoles.stream()
        .filter((c) -> c.name().contains("16 bits"))
        .map((c) -> c.name())
        .toList();

    System.out.println("\nQ3 - 16 bits consoles: ");
    sixteenBitsConsoleNames.stream().forEach((g) -> System.out.println("- " + g));

    System.out.println("\nQ4 - Nom du premier jeu sorti : ");
    Optional<VideoGame> minYearVideoGame = games.stream().min(Comparator.comparingInt((game) -> game.releaseYear()));
    if (!minYearVideoGame.isPresent()) {
      return;
    }
    System.out.println(minYearVideoGame.get().name());

    var minYearVideosGames = games.stream()
        .filter((g) -> g.releaseYear() == minYearVideoGame.get().releaseYear()).toList();
    System.out.println("\nQ4 ++ - Au cas où il y a plusieurs jeux sortis la même année : ");
    System.out.println(Joiner.on(",").join(minYearVideosGames));

    System.out.println("\nQ5 - Nombre de jeux de chaque éditeur : ");
    var groupedGamesByPublishers = games.stream()
        .collect(Collectors.groupingBy((g) -> g.publisher()))
        .entrySet().stream()
        .map((entry) -> new AbstractMap.SimpleEntry<String, Integer>(entry.getKey(), entry.getValue().size())).toList();
    var groupedGamesByPublishers2 = games.stream()
        .collect(Collectors.groupingBy(VideoGame::publisher, Collectors.counting()));
    System.out.println(Joiner.on(",").join(groupedGamesByPublishers));
    System.out.println(Joiner.on(",").join(groupedGamesByPublishers2.entrySet()));
    groupedGamesByPublishers2.forEach((k, v) -> System.out.println(k + " number of games: " + v));

    // Map of first game for each published
    var firstGameByPublishers = games.stream().collect(
        Collectors.groupingBy(VideoGame::publisher,
            Collectors.mapping(VideoGame::releaseYear, Collectors.minBy(Comparator.comparingInt(year -> year)))));
    System.out.println("\nQ5 - Année du premier jeu de chaque éditeur : ");
    System.out.println(Joiner.on(",").join(firstGameByPublishers.entrySet()));
  }
}

Exercice 2

Les méthodes Stream.iterate et Stream.generate combinées avec limite permettent de créer des streams. Voici quelques exemples d'utilisation de ces méthodes:

// This will create a stream of 10 elements starting from -4 and incrementing by 2
Stream<Integer> streamIterated = Stream.iterate(-4, n -> n + 2).limit(10);
streamIterated.forEach(System.out::println);

// This will create a stream of 10 randome integers between 0 and 100
Stream<Integer> streamGenerated2 = Stream.generate(() -> RandomGenerator.getDefault().nextInt(0, 100)).limit(10);
streamGenerated2.forEach(System.out::println);

Répondez aux questions suivantes en utilisant les streams (pas de boucle for):

  1. Créer un Stream de 10 entiers aléatoires compris entre -10 et 10. Afficher la somme, le min, le max et les éléments supérieurs à la moyenne.
  2. Créer un Stream<Character> qui génère 10 voyelles aléatoires. Compter le nombre de fois qu'apparaît la lettre a.

Exercice 3

La classe IntStream est une classe spécialisée sur les streams d'entiers. Elle est également le résultat d'une conversion d'une string en stream (chaque caractère est représenté par son code en entier). Voici quelques exemples d'utilisation de cette classe:

// This will create a stream of 10 elements starting from -4 and incrementing by 2
IntStream streamIterated = IntStream.iterate(-4, n -> n + 2).limit(10);
streamIterated.forEach(System.out::println);

// This will create a stream of 10 randome integers between 0 and 100
IntStream streamGenerated2 = IntStream.generate(() -> RandomGenerator.getDefault().nextInt(0, 100)).limit(10);
streamGenerated2.forEach(System.out::println);

//Convert a string to a stream of characters (each character is an integer)
IntStream streamOfChars = "hello".chars();
streamOfChars.forEach(System.out::println);

//count the number of l in a string
long count = "hello".chars().filter(ch -> ch == 'l').count();
System.out.println(count);

Répondez aux questions suivantes en utilisant les streams (pas de boucle for):

  1. Calculer le nombre de voyelles dans une chaîne de caractères.
  2. Générer un IntStream de 10 voyelles aléatoires. Compter le nombre de fois qu'apparaît la lettre a.
  3. Calculer le nombre d'occurences de chaque lettre dans une chaîne de caractères.