Skip to main content

Stream Exercice

Contexte

Soit le model de donnée suivant:

@Data
@Entity
@NoArgsConstructor
public class Customer {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  
  private String name;
  private Integer tier;
}

@Data
@NoArgsConstructor
@Entity
@Table(name = "product_order")
public class Order {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  
  private LocalDate orderDate;
  private LocalDate deliveryDate;
  private String status;
  
  @ManyToOne
  @JoinColumn(name = "customer_id")
  private Customer customer;
  
  @ManyToMany
  @JoinTable(
      name = "order_product_relationship",
      joinColumns = { @JoinColumn(name = "order_id") },
      inverseJoinColumns = { @JoinColumn(name = "product_id") }
  )
  @ToString.Exclude
  Set<Product> products;
    
}


@Data
@NoArgsConstructor
@Entity
public class Product {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  
  private String name;
  private String category;
  @With private Double price;
  
  @ManyToMany(mappedBy = "products")
  @ToString.Exclude
  private Set<Order> orders;
}

Opération Non terminal

  • filter()
  • map()
  • flatMap
  • distinct()
  • sorted()
  • peek()

Operation terminal

  • anyMatch()
  • collect()
  • count()
  • findFirst()
  • min()
  • max()
  • sum()
  • average()

Exercice 1 — Obtenir une liste de produits appartenant à la catégorie "Livres" avec un prix > 100

L'idée est d'utiliser un double filter et une collection.

Solution

List<Product> result = productRepo.findAll()
  .stream()
  .filter(p -> p.getCategory().equalsIgnoreCase("Livres"))
  .filter(p -> p.getPrice() > 100)
  .collect(Collectors.toList());

 

Exercice 2 — Obtenir une liste de commandes avec des produits appartenant à la catégorie "Bébé"

Vous devez partir du flux de données des entités de la commande, puis vérifier si les produits de la commande appartiennent à la catégorie "Bébé". Par conséquent, la logique de filtrage examine le flux de produits de chaque enregistrement de commande et utilise anyMatch() pour déterminer si un produit remplit les critères.

Solution

  List<Order> result = orderRepo.findAll()
        .stream()
        .filter(o -> 
          o.getProducts()
          .stream()
          .anyMatch(p -> p.getCategory().equalsIgnoreCase("Bébé"))
        )
        .collect(Collectors.toList());  

Exercice 3 - Obtenez une liste de produits avec la catégorie = "Jouets" puis appliquez une remise de 10 %

Dans cet exercice, vous verrez comment transformer des données à l'aide de l'API de flux. Après avoir obtenu une liste de produits avec une catégorie appartenant à "Jouets" à l'aide de filter(), vous pouvez ensuite appliquer une remise de 10 % sur le prix du produit en utilisant map().

 

Solution

  List<Product> result = productRepo.findAll()
        .stream()
        .filter(p -> p.getCategory().equalsIgnoreCase("Jouets"))
        .map(p -> p.withPrice(p.getPrice() * 0.9))
        .collect(Collectors.toList());  
  

Exercice 4 — Obtenir une liste des produits commandés par le client du niveau 2 entre le 1er février 2021 et le 1er avril 2021

Cet exercice illustre l'utilisation de flatMap(). Vous pouvez d'abord partir d'une liste de commandes, puis filtrer la liste par niveau de client et date de commande. Ensuite, récupérez la liste des produits de chaque enregistrement de commande et utilisez flatMap() pour émettre des enregistrements de produits dans le flux. Par exemple, si nous avons 3 enregistrements de commande et que chaque commande contient 10 produits, alors flatMap() émettra 10 éléments de données pour chaque enregistrement de commande, ce qui entraînera une sortie d'enregistrement de produit de 30 (3 x 10) dans le flux.

Étant donné que la liste de produits contiendrait des enregistrements de produits en double si plusieurs commandes incluaient le même produit. Afin de générer une liste de produits unique, l'application de l'opération distinct() peut aider à produire la liste unique.

Solution

 List<Product> result = orderRepo.findAll()
  .stream()
  .filter(o -> o.getCustomer().getTier() == 2)
  .filter(o -> o.getOrderDate().compareTo(LocalDate.of(2021, 2, 1)) >= 0)
  .filter(o -> o.getOrderDate().compareTo(LocalDate.of(2021, 4, 1)) <= 0)
  .flatMap(o -> o.getProducts().stream())
  .distinct()
  .collect(Collectors.toList());

Exercice 5 — Obtenir les produits les moins chers de la catégorie « Livres »

L'un des moyens efficaces d'obtenir le produit au prix le plus bas est de trier la liste de produits par prix dans un ordre croissant et d'obtenir le premier élément. L'API Java Stream fournit une opération sorted () pour le tri des données de flux en fonction d'attributs de champ spécifiques. Afin d'obtenir le premier élément du flux, vous pouvez utiliser l'opération de terminal findFirst(). L'opération renvoie le premier élément de données enveloppé par Facultatif car il est possible que le flux de sortie soit vide.

Cette solution ne peut renvoyer qu'un seul enregistrement de produit avec le prix le plus bas. S'il existe plusieurs enregistrements de produit avec le même prix le plus bas, la solution doit être modifiée de sorte qu'elle recherche d'abord le montant du prix le plus bas, puis filtre les enregistrements de produit par le montant du prix afin d'obtenir une liste de produits avec le même prix le plus bas.

Solution

 Optional<Product> result = productRepo.findAll()
        .stream()
        .filter(p -> p.getCategory().equalsIgnoreCase("Livres"))
        .sorted(Comparator.comparing(Product::getPrice))
        .findFirst();

ou

  Optional<Product> result = productRepo.findAll()
        .stream()
        .filter(p -> p.getCategory().equalsIgnoreCase("Livres"))
        .min(Comparator.comparing(Product::getPrice));

 

Exercice 6 — Obtenir les 3 dernières commandes passées

Semblable à l'exercice précédent, la solution évidente consiste à trier les enregistrements de commande par champ de date de commande. La partie délicate est que le tri doit cette fois être dans l'ordre décroissant afin que vous puissiez obtenir les enregistrements de commande avec la date de commande la plus récente. Cela peut être réalisé simplement en appelant Comparator.reversed().

 List<Order> result = orderRepo.findAll()
        .stream()
        .sorted(Comparator.comparing(Order::getOrderDate).reversed())
        .limit(3)
        .collect(Collectors.toList());

Exercice 7 - Obtenez une liste des commandes qui ont été commandées le 15 mars 2021, enregistrez les enregistrements de commande sur la console, puis renvoyez sa liste de produits

Vous pouvez voir que cet exercice implique deux actions - (1) écrire des enregistrements de commande sur la console et (2) produire une liste de produits. Générer une sortie différente à partir d'un flux n'est pas possible, comment pouvons-nous répondre à cette exigence ? En plus d'exécuter le flux de flux deux fois, l'opération peek() permet l'exécution de la logique système dans le cadre d'un flux de flux. L'exemple de solution exécute peek() pour écrire les enregistrements de commande dans la console juste après le filtrage des données, puis les opérations suivantes telles que flatMap() seront exécutées pour la sortie des enregistrements de produit.

 List<Product> result = orderRepo.findAll()
    .stream()
    .filter(o -> o.getOrderDate().isEqual(LocalDate.of(2021, 3, 15)))
    .peek(o -> System.out.println(o.toString()))
    .flatMap(o -> o.getProducts().stream())
    .distinct()
    .collect(Collectors.toList());