• Home
  • LLMs
  • Python
  • Docker
  • Kubernetes
  • Java
  • Maven
  • All
  • About
Java | Streams
  1. L'interface Stream
  2. Package "java.util.stream"
  3. Méthodes de l'interface Stream
  4. Utilisation des streams avec les collections

  1. L'interface Stream
    Java permet de construire un flux de traitement (ou un pipeline) qui contient un ensemble d'étapes (ou opérations) qui s’appliquent en série sur les éléments d’une source de données.

    Les opérations qui peuvent être appliquées sur un flux sont de deux types : des opérations intermédiaires (intermediate operations) ou des opérations terminales (terminal operation).

    • Le flux de traitement commence par créer une instance de l’interface java.util.stream.Stream à partir d’une source de données (collection, tableau, ensemble d’éléments).

      Exemples :
      // Creates a stream from a set of values.
      Stream<Integer> integerStream1 = Stream.of(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
      
      // Creates a stream from an array.
      Integer[] values = {1, 2, 3};
      Stream<Integer> integerStream2 = Stream.of(values);
      Stream<Integer> integerStream3 = Arrays.stream(values);
      
      // Creates a stream from a list.
      Stream<Integer> integerStream4 = Arrays.asList(values).stream();
    • Les opérations intermédiaires du pipeline produisent une nouvelle instance de l’interface Stream.

      Exemples :
      Stream<Integer> integerStream1 = Stream.of(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
      
      // the intermediate operation "filter" transform a stream into another stream
      Stream<Integer> integerStream2 = integerStream1.filter(i -> i%2==0);
      
      // the intermediate operation "map" transform a stream into another stream
      Stream<String> integerStream3 = integerStream2.map(i -> String.valueOf(i));
    • L’opération terminale du pipeline produit un résultat ou un effet de bord. Cette opération ferme le flux.

      Exemples :
      // the terminal operation "count" produces a result
      Long value = Stream.of(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)).count();
      
      // the terminal operation "forEach" produces a side-effect
      Stream.of(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)).forEach(System.out::println);
    • Les opérations intermédiaires sont exécutées uniquement si l'opération terminale est initiée. Autrement dit, les opérations intermédiaires d’un pipeline ne seront jamais exécutées si le pipeline ne declare pas une operation terminale.

      Aussi les éléments de la source de données sont consommés uniquement au besoin.

      Exemples :
      // filter and limit are intermediate operations, they won't be applied now
      // the intermediate operation "limit" will restrict the processing of the
      // elements of the stream to only the specified number
      Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 4, 5, 6).filter(i -> i % 2 == 0).limit(2);
      
      // forEach is a terminal operation, all intermediate operations along with the
      // terminal operation will be applied
      integerStream1.forEach(System.out::println);
    • Une seule opération terminale peut s'appliquer sur une instance Stream.

      Si une deuxième opération (intermédiaire ou terminale) est appliquée sur une instance Stream après l'exécution de l'opération terminale, une exception va être déclenchée :
      "java.lang.IllegalStateException: stream has already been operated upon or closed".

      Exemples :
      Stream<Integer> integerStream1 = Stream.of(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
      
      // OK: first operation to be applied on the "integerStream1" instance
      Stream<Integer> integerStream2 = integerStream1.filter(i -> i%2==0);
      
      // throws the exception: java.lang.IllegalStateException: stream has already been operated upon or closed
      Stream<Integer> integerStream3 = integerStream1.filter(i -> i%2==0);
      
      // throws the exception: java.lang.IllegalStateException: stream has already been operated upon or closed
      long i = integerStream1.count();
      
      // OK: first operation to be applied on the "integerStream2" instance
      System.out.println(integerStream2.count());
      
      // throws the exception: java.lang.IllegalStateException: stream has already been operated upon or closed
      System.out.println(integerStream2.count());
    • La chaine des operations qui peuvent être appliquées sur une séquence des éléments peuvent être exécutées en série (par défaut) ou en parallèle.

      Exemples :
      Integer[] values = {1, 2, 3};
      
      // stream returns a sequential Stream
      Arrays.stream(values).forEach(System.out::println);
      Arrays.asList(values).stream().forEach(System.out::println);
      
      // parallel/parallelStream returns a possibly parallel Stream
      Arrays.stream(values).parallel().forEach(System.out::println);
      Arrays.asList(values).parallelStream().forEach(System.out::println);
  2. Package "java.util.stream"
    La librairie Java fournit une liste des interfaces pour manipuler les streams qui se trouvent dans le package "java.util.stream":

    • BaseStream: BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable

      Interface de base pour les classes qui manipulent les flux.
      public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {
          Iterator<T> iterator();
      
          Spliterator<T> spliterator();
      
          boolean isParallel();
      
          S sequential();
      
          S parallel();
      
          S unordered();
      
          S onClose(Runnable closeHandler);
      
          @Override
          void close();
      }
      • Stream: Stream<T> extends BaseStream<T, Stream<T>>
        C'est la principale spécialisation de l'interface "BaseStream".
        Elle définit plusieurs méthodes de transformation, filtrage, agrégation, … (filter, map, reduce, distinct, count, ...).
        public interface Stream<T> extends BaseStream<T, Stream<T>> {
          ...
        }

      • IntStream: IntStream extends BaseStream<Integer, IntStream>
        Cette interface est une spécialisation de l'interface "BaseStream" en spécifiant le type du parameter "T" a "Integer".
        Elle définit plusieurs méthodes de transformation, filtrage, agrégation, … (filter, map, reduce, distinct, count, ...).
        public interface IntStream extends BaseStream<Integer, IntStream> {
          ...
        }

      • LongStream: LongStream extends BaseStream<Long, LongStream>
        Cette interface est une spécialisation de l'interface "BaseStream" en spécifiant le type du parameter "T" a "Long".
        Elle définit plusieurs méthodes de transformation, filtrage, agrégation, … (filter, map, reduce, distinct, count, ...).
        public interface LongStream extends BaseStream<Long, LongStream> {
          ...
        }

      • DoubleStream: DoubleStream extends BaseStream<Double, DoubleStream>
        Cette interface est une spécialisation de l'interface "BaseStream" en spécifiant le type du parameter "T" a "Double".
        Elle définit plusieurs méthodes de transformation, filtrage, agrégation, … (filter, map, reduce, distinct, count, ...).
        public interface DoubleStream extends BaseStream<Double, DoubleStream> {
          ...
        }
  3. Méthodes de l'interface Stream
    • collect (opération terminale)
      L’API java définit deux versions de la fonction collect.
      public interface Stream<T> extends BaseStream<T, Stream<T>> {
          <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
      
          <R, A> R collect(Collector<? super T, A, R> collector);
      }
      • La première méthode prend trois paramètres:
        <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);

        Exemples :
        Stream<Integer> integerStream = Stream.of(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
        
        Supplier<StringBuilder> supplier = StringBuilder::new;
        BiConsumer<StringBuilder, ? super Integer> accumulator = StringBuilder::append;
        BiConsumer<StringBuilder, StringBuilder> combiner = StringBuilder::append;
        
        StringBuilder stringBuilder = integerStream.collect(supplier, accumulator, combiner);

      • La deuxième méthode prend un seul paramètre:
        <R, A> R collect(Collector<? super T, A, R> collector);

        Exemples :
        Stream<Integer> integerStream = Stream.of(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
        
        List<Integer> list = integerStream.collect(Collectors.toList());
        //Collectors.toList() returns new CollectorImpl<>(ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, CH_ID)

    • map (opération intermédiaire)
      public interface Stream<T> extends BaseStream<T, Stream<T>> {
          <R> Stream<R> map(Function<? super T, ? extends R> mapper);
      }
      Cette méthode applique la fonctionne passée en paramètre sur les éléments du flux et produit un nouveau flux dont les éléments sont le résultat de l’application de cette fonction.

      Exemples :
      Stream<Integer> intStream = Stream.of(1, 2, 3);
      Stream<String> stringStream = intStream.map(String::valueOf);
      stringStream.forEach(System.out::println);

    • filter (opération intermédiaire)
      public interface Stream<T> extends BaseStream<T, Stream<T>> {
          Stream<T> filter(Predicate<? super T> predicate);
      }
      Cette méthode applique la fonctionne passée en paramètre qui sert a filtrer les éléments du flux en gardant uniquement ceux qui correspondent au prédicat donné.

      Exemples :
      Stream<Integer> intStream1 = Stream.of(1, 2, 3, 4, 5);
      Stream<Integer> intStream2 = intStream1.filter(i -> i % 2 == 0);
      intStream2.forEach(i -> System.out.println(i));

    • findFirst / findAny (opérations terminales)
      public interface Stream<T> extends BaseStream<T, Stream<T>> {
          Optional<T> findFirst();
      
          Optional<T> findAny();
      }
      findFirst retourne le premier élément du flux.
      findAny retourne arbitrairement un seul élément du flux.

      Ces deux opérations sont des opérations terminales qui arrêtent le traitement sur le flux dès qu’un element est trouvé.

      Exemples :
      Optional<Integer> firstValue = Stream.of(1, 2, 3).findFirst();
      System.out.println(firstValue.get());
      
      Optional<Integer> anyValue = Stream.of(1, 2, 3).findAny();
      System.out.println(anyValue.get());

    • anyMatch / allMatch / noneMatch (opérations terminales)
      public interface Stream<T> extends BaseStream<T, Stream<T>> {
          boolean anyMatch(Predicate<? super T> predicate);
      
          boolean allMatch(Predicate<? super T> predicate);
      
          boolean noneMatch(Predicate<? super T> predicate);
      }
      anyMatch retourne true si au moins un seul élément du flux correspond au prédicat fourni.
      allMatch retourne true si tous les éléments du flux correspondent au prédicat fourni.
      noneMatch retourne true si aucun des éléments du flux ne correspond au prédicat fourni.

      Ces trois opérations sont des opérations terminales qui arrêtent le traitement sur le flux dès qu’un élément du flux correspond au prédicat fourni (pour l'opération anyMatch et noneMatch) ou qu'un élément du flux ne correspond pas au prédicat fourni (pour l'opération allMatch).

      Exemples :
      // anyMatch
      boolean anyMatch = Stream.of(1, 2, 3).anyMatch(i -> i % 2 == 1);
      System.out.println(anyMatch);
      
      // allMatch
      boolean allMatch = Stream.of(1, 2, 3).allMatch(i -> i % 2 == 1);
      System.out.println(allMatch);
      
      // noneMatch
      boolean noneMatch = Stream.of(1, 2, 3).noneMatch(i -> i % 2 == 1);
      System.out.println(noneMatch);
  4. Utilisation des streams avec les collections
    • stream / parallelStream
      L'interface java.util.Collection définit une implementation par défaut des méthodes stream et parallelStream.
      public interface Collection<E> extends Iterable<E> {
          default Stream<E> stream() {
              return StreamSupport.stream(spliterator(), false);
          }
      
          default Stream<E> parallelStream() {
              return StreamSupport.stream(spliterator(), true);
          }
      }
      Les méthodes stream et parallelStream retournent une instance de java.util.stream.Stream dont la source est la collection courante.

      Exemples :
      Collection<Integer> list = Arrays.asList(3, 1, 2);
      
      list.stream().sorted().forEach(System.out::println);
      
      list.parallelStream().forEach(System.out::println);
© 2025  mtitek