[JAVA] J'ai essayé de résumer l'API Stream

Qu'est-ce que l'API Stream?

Stream API, comme son nom l'indique, est un groupe d'API pour le traitement des données et des événements en flux appelé Stream. Ajouté dans Java SE 8 Le flux est généré sur la base d'un ensemble de données tel que List ou Map, et le résultat est obtenu en exécutant 0 ou plusieurs opérations intermédiaires et 1 opération de terminaison.

Comment utiliser l'API Stream

Après avoir obtenu un aperçu approximatif, examinons la source et comparons le traitement utilisant stream avec le traitement ne l'utilisant pas. À titre d'exemple, j'écrirai le code qui traite List et le traite un par un.

Aucun flux


		List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
		for (Integer i : integerList) {
		    if (i % 2 == 0) {
		        System.out.println(i);
		    }

description du flux


		List<Integer> integerList = Arrays.asList(1,2,3,4);
		integerList.stream()
		           .filter(i -> i % 2 ==0)
		           .forEach(i -> System.out.println(i));

Résultat de sortie


2
4

C'est un code qui transforme une simple liste avec une instruction for, rend un jugement avec une instruction if et la produit. La partie à traiter en tournant avec l'instruction for peut être remplacée par stream, et l'instruction if réduit directement les éléments internes avec filtre et génère les éléments restants avec forEach.

Le premier peut être plus facile à voir pour ceux qui sont habitués à l'environnement avant Java8, mais le second deviendra plus facile à utiliser et à comprendre à mesure que vous vous habituerez à écrire des flux. Dans stream, les méthodes sont divisées en unités qui vous permettent de savoir quoi faire, et si vous supprimez l'utilisation de chaque méthode, c'est une méthode de description qui vous permet de saisir intuitivement le contenu du traitement.

L'API Stream est utilisée en exécutant en continu les processus de «création», «manipulation» et «combinaison» d'un Stream pour une structure de données.

Générer l'opération

Il existe à peu près les méthodes suivantes pour créer un flux.

java.util.stream.Stream#of(T...) : Stream java.util.stream.Stream.Builder#build() : Stream java.util.Collection#stream() : Stream java.util.Arrays#stream(T[]) : Stream java.io.BufferedReader#lines() : Stream java.nio.Files#lines(Path) : Stream java.nio.Files#lines(Path, Charset) : Stream java.util.regex.Pattern#splitAsStream(CharSequence) : Stream java.lang.CharSequence#chars() : IntStream java.lang.CharSequence#charPoints() : IntStream

Cette fois, je présenterai deux cas, l'un est de créer à partir d'une interface de collection telle que List et Set, qui sont souvent utilisés, et l'autre est de créer à partir d'un tableau. Pour créer un flux à partir d'une liste ou d'un ensemble, utilisez la méthode stream (). Si vous souhaitez créer un Stream à partir d'un tableau, utilisez la méthode Arrays.stream ().

python


		//Traitement des flux de listes
		List<Integer> numbers = List.of(3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5);
		Stream<Integer> stream = numbers.stream();
		//Traitement Steram des tableaux
		int[] array = {3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5};
		IntStream stream = Arrays.stream(array);

Si vous souhaitez créer un flux à partir d'une carte, il n'y a pas d'API pour créer un flux directement à partir de la carte, créez donc un flux à partir de Set obtenu à l'aide de la méthode entrySet de la carte.

		Map<String, Integer> map = Map.of("key1", 3, "ke2", 1, "key3", -4, "key4", 1);
		Stream<Entry<String, Integer>> stream = map.entrySet().stream();

Vous pouvez utiliser la méthode Files.line pour lire un fichier texte ligne par ligne et en faire un flux de chaînes.

		Stream<String> lines = Files.lines(Path.of("/tmp/test.txt"));

Fonctionnement intermédiaire

Le processus exécuté sur Stream est appelé une opération intermédiaire. Les opérations intermédiaires ont les méthodes suivantes.

peek(Consumer<? super T> action) skip(long maxSize) limit(long maxSize) filter(Predicate<? super T> predicate) distinct() sorted() / sorted(Comparator<? super T> comparator) map(Function<? super T,? extends R> mapper) flatMap(Function<? super T,? extends Stream<? extends R>> mapper) parallel() sequencial() unordered()

Cette fois, la méthode fréquemment utilisée Je vais me concentrer sur le tri, la carte et le filtre.

1.sorted() sorted () renvoie un Stream avec des éléments triés du Stream. Il y a deux surcharges pour sorted (), avec et sans arguments.

Sorted () sans argument trie simplement les éléments par ordre naturel croissant. (Cependant, la classe d'élément doit être Comaprable.

Si vous souhaitez trier par ordre "non naturel" ou décroissant, passez un comparateur personnalisé en tant que fonction à soreted () avec des arguments. (Si vous le donnez en lambda, il est plus simple d'utiliser Comparator # compare ().)

		List<String> array = Arrays.asList("a","1","-2","Ah","A","123");

		System.out.println("--Aucun argument--");
		//Aucun argument
		array.stream()
			.sorted()  //Ordre naturel ordre croissant
			.forEach(System.out::println);

		System.out.println("--Comparator--");
	    // Comparator
		array.stream()
	            .sorted(Comparator.reverseOrder()) //Ordre décroissant naturel
	            .forEach(System.out::println);

		System.out.println("--Style Lambda--");
	    //Style Lambda
		array.stream()
	            .sorted((l, r) -> l.length() - r.length()) //Ordre décroissant de la longueur de la chaîne de caractères
	            .forEach(System.out::println);

		System.out.println("--Référence de la méthode--");
	    //Référence de la méthode(Comparable)
		array.stream()
	            .sorted(String::compareToIgnoreCase) //Ignorer la casse
	            .forEach(System.out::println);

		System.out.println("--Objet de fonction--");
	    //Objet de fonction
		array.stream()
	            .sorted(String.CASE_INSENSITIVE_ORDER) //Ignorer la casse
	            .forEach(System.out::println);

	    // java.util.List`Aussi 1.À 8`sort()`Est ajouté avec la méthode par défaut.
	    //C'est plus économique si le but est de changer l'ordre des éléments de la liste elle-même.
		System.out.println("--Trier par liste--");
	    //Spécifier le comparateur
		array.sort(Comparator.reverseOrder());
	    array.stream()
	    	.forEach(System.out::println);

Résultat de sortie


--Aucun argument--
-2
1
123
A
a
Ah
--Comparator--
Ah
a
A
123
1
-2
--Style Lambda--
a
1
Ah
A
-2
123
--Référence de la méthode--
-2
1
123
a
A
Ah
--Objet de fonction--
-2
1
123
a
A
Ah
--Trier par liste--
Ah
a
A
123
1
-2

2.filter()/distinct()

filter () et distinct () éclaircissent les éléments de Stream par leur contenu.

filter () est une opération intermédiaire qui réduit les éléments en fonction des conditions et donne une fonction de prédicat (Predicate) pour le jugement comme argument. Tant que le prédicat ne regarde que la valeur de l'élément, filter () est une opération intermédiaire sans état. distinct () est une opération intermédiaire qui élimine les éléments en double.

        System.out.println("--filter--");
		List array = Arrays.asList("a.txt","b.com","c.new");
        array.stream()
        .filter(s -> ((String) s).endsWith(".txt")) //Style Lambda
        .forEach(System.out::println);

        System.out.println("--distinct--");
        String data = "aaa aaa bbb aaa ccc bbb ccc ccc";
        String uniq = Stream.of(data.split("\\s"))
                .peek(s -> System.out.print("\n" +  s))
                .distinct()
                .peek(s -> System.out.print("\t" + s))
                .collect(Collectors.joining(","));
        System.out.println();
        System.out.println(uniq);

Résultat d'exécution


--filter--
a.txt
--distinct--

aaa	aaa
aaa
bbb	bbb
aaa
ccc	ccc
bbb
ccc
ccc
aaa,bbb,ccc

3.map()

map () renvoie un Stream dont les éléments ont été transformés par la fonction donnée. Le type de retour de la fonction peut changer le type de l'élément Stream.

	    //Préparation de la fonction (mettre en majuscule le premier caractère)
	    Function<String, String> capitalize = (String s) -> {
	        return String.format("%c%s",
	                Character.toTitleCase(s.charAt(0)),
	                s.substring(1));
	    };

	    List<String> words = Arrays.asList("aasDfag");
	    words = words.stream()
	    		.peek(System.out::println)
	            .map(s -> s.trim())        //Style Lambda
	            .filter(s -> !s.isEmpty())
	            .map(String::toLowerCase)  //Référence de la méthode
	            .peek(System.out::println)
	            .map(capitalize)           //Objet de fonction
	            .collect(Collectors.toList());
	    System.out.println(words);

Résultat d'exécution


aasDfag
aasdfag
[Aasdfag]

Opération de terminaison

L'opération de terminaison peut être grossièrement divisée en quatre processus.

--Chercher --Agrégation

Cette fois, je présenterai les méthodes en quatre parties.

1. Rechercher

1.1. findFirst()/findAny()

findFirst () renvoie le premier élément comme facultatif. findAny () renvoie le premier élément comme facultatif. Facultatif peut être vide.

	    String[] words = {"aaaaaa", "bbbbbb", "cccccc"};

	    List<String> list = Arrays.asList(words);
	    Optional<String> first = list.stream().findFirst();
	    first.ifPresent(s -> {
	        System.out.println(s);  // "aaaaaa"
	    });

	    Set<String> set = new HashSet<>(list);
	    Optional<String> any = set.stream().findAny();
	    any.ifPresent(s -> {
	        System.out.println(s);  // "cccccc"
	    });

1.2. allMatch() / anyMatch() / noneMatch()

allMatch () / anyMatch () / noneMatch () recherche l'élément de résultat Stream en fonction de la fonction de prédicat donnée (Predicate) et détermine l'état d'existence de l'élément correspondant.

	    List<String> list = Arrays.asList("a","asdf","");
	    boolean ok;

	    //Style Lambda
	    ok = list.stream()
	            .allMatch(s -> s != null && !s.isEmpty()); //Ne contient pas de chaînes nulles et vides
	    System.out.println(ok);
	    //Référence de la méthode
	    ok = list.stream()
	            .allMatch(Objects::nonNull);    //Ne contient pas de valeur nulle
	    System.out.println(ok);
	    //Fonction de prédicat
	    ok = list.stream()
	            .noneMatch(Predicate.isEqual("")); //Peut être nul et ne contient pas de chaînes vides
	    System.out.println(ok);

Résultat d'exécution


false
true
false

2. Agrégation

2.1. count() / min() / max()

count () compte littéralement le nombre d'éléments Stream. Comme cela compte vraiment, cela prend un certain temps de traitement.

    //Nombre de lignes dans le fichier texte
    int lc = (int) Files.lines(Paths.get("text.txt")).count();
    //Nombre de mots dans le texte
    int wc = (int) Pattern.compile("\\W+").splitAsStream(text).count();
    //Comptez la différence en lettres
    int vc = (int) text.codePoints().distinct().count();

Passez une fonction de comparaison (Comparator) à min () / max () pour obtenir les valeurs maximale et minimale des éléments. La valeur de retour est facultative , qui est vide s'il n'y a aucun élément. Le flux est toujours chargé jusqu'à la fin.

    List<String> list = ... ;
    Optional<String> min;

    // Comparator
    min = list.stream()
            .min(Comparator.naturalOrder()); //La plus petite chaîne dans l'ordre lexical
    //Référence de la méthode
    min = list.stream()
            .min(String::compareToIgnoreCase); //Insensible à la casse
    //Style Lambda
    min = list.stream()
            .min((l, r) -> l.length() - r.length()); //Chaîne la plus courte
    // Comparable
    min = list.stream()
            .min(Comparator.comparing(s -> s.toUpperCase())); //Insensible à la casse

Les flux primitifs tels que DoubleStream fournissent des opérations de terminaison telles que sum () et average () ainsi que count () / min () / max ().

Il est pratique d'obtenir la valeur agrégée sans boucle, mais si vous essayez de calculer à l'aide de la valeur agrégée, vous devrez exécuter Stream plusieurs fois, ce qui est inefficace. Pour cette raison, il existe également une opération de terminaison appelée summaryStatistics () qui peut prendre chaque valeur agrégée en un seul coup.

2.2. reduce()

Utilisez réduire () si vous souhaitez appliquer une fonction d'agrégation personnalisée. Par exemple, il peut être utilisé pour le traitement de concaténation qui ne peut pas être simplement réalisé par jointure.

    //Extraire le dernier élément
    Optional<String> last = stream.reduce((l, r) -> r);

    //Convertir le nom de domaine en nom de package
    String domain = "hoge.example.co.jp";
    //Inverser l'ordre des éléments
    String pkg = Stream.of(domain.split("\\."))
            .reduce((l, r) -> r + "." + l).get();
    // jp.co.example.hoge

Pour être honnête, je ne comprenais pas comment l'utiliser. .. Je vais le chercher plus tard et l'ajouter à l'article.

3. Convertir

3.1. toArray()

toArray () convertit Stream en un tableau de ses éléments.


Stream<String> stream = Stream.of("a", "b", ...);

//Aucun argument
Object[] arr = stream.toArray();

//Voir le constructeur de tableaux
String[] arr = stream.toArray(String[]::new);

//Style Lambda
String[] arr = stream.toArray((size) -> new String[size]);

3.2. collect() Convertir du flux en liste. Pour l'argument, appelez et définissez la méthode collectée en tant que collection avec la méthode de fabrique statique dans `` java.util.stream.Collectors ''.

Java Collector Memo (Java8 Collector Memo de Hishidama)

//Vous pouvez omettre le nom de la classe en important des collecteurs de manière statique.
//Caractères génériques en fonction des paramètres IDE(*)Peut ne pas vous laisser utiliser.
import static java.util.stream.Collectors.*;

    Stream<String> stream = ... ;

    //Convertir en chaîne(Mise en relation)
    String text = stream.collect(joining());

    //Convertir en chaîne(Spécification du délimiteur)
    String csv = src.stream().collect(joining(", "));

    //Convertir en liste
    List<String> list = stream.collect(toList()); // ArrayList

    //Convertir en n'importe quelle classe List
    List<String> list = stream
            .collect(toCollection(LinkedList::new)); // LinkedList

    //Convertir en ensemble
    Set<String> set = stream.collect(toSet());  // HashSet

    //Convertir en n'importe quelle classe Set
    SortedSet<String> set = stream
            .collect(toCollection(TreeSet::new)); //TreeSet trié

    LinkedHashSet<String> set = stream
            .collect(toCollection(LinkedHashSet::new)); //Maintenir l'ordre des éléments LinkedHashSet

    //Convertir en carte
    // id -> object
    Map<Integer, User> map = users.stream()
            .collect(toMap(
                    e -> e.getId(),  //Exception si la clé est dupliquée
                    e -> e           // value
            )); // HashMap

    // id -> name
    Map<Integer, User> map = users.stream()
            .collect(toMap(User::getId, User::getName));

    //Convertir en n'importe quelle classe de carte
    SortedMap<Integer, User> map = users.stream()
            .collect(toMap(
                    e -> e.getId(),
                    e -> e,
                    (l, r) -> r, //Écraser si la clé est dupliquée
                    TreeMap::new
            )); // TreeMap

4. Sortie

4.1. forEach() / forEachOrdered()

forEach () est une opération de terminaison qui peut générer et convertir les données de flux envoyées.


    List<String> list = Arrays.asList("a", "b", "c");

    //Diffuser pour chaque()Est un effet secondaire
    //Je veux clarifier à l'aide de blocs
    list.stream()
            .forEach(s -> {
                System.out.println(s);
            });

    //Si vous n'utilisez pas de blocs, vous pouvez le voir et renvoyer la valeur.
    list.stream().forEach(s -> System.out.println(s));

    // forEach()Est difficile d'utiliser des références de méthode
    list.stream().forEach(System.out::println);

    //Itérable pour chaque()
    list.forEach(s -> {
        System.out.println(s);
    });

    //Extension équivalente pour la syntaxe
    for (String s : list) {
        System.out.println(s);
    }

    Map<String, String> map = new HashMap<>();

    //En fait, c'est aussi dans la carte
    map.forEach((key, val) -> {
        System.out.format("%s=%s\n", key, val);
    });

    //Je souhaite utiliser Stream avec Map
    map.entrySet().stream()
            .forEach(e -> {
                System.out.format("%s=%s\n", e.getKey(), e.getValue());
            });

référence

Principes de base de l'API Stream J'ai essayé de diffuser le texte vers l'API Java 8 Stream (opération de terminaison)

Recommended Posts

J'ai essayé de résumer l'API Stream
[java8] Pour comprendre l'API Stream
J'ai essayé d'expliquer la méthode
J'ai essayé d'utiliser l'API Java8 Stream
J'ai essayé de résumer l'apprentissage Java (1)
J'ai essayé de résumer Java 8 maintenant
J'ai brièvement résumé la grammaire de base de Ruby
J'ai essayé de résumer les expressions Java lambda
J'ai essayé d'implémenter le modèle Iterator
Qu'est-ce que Docker? J'ai essayé de résumer
J'ai essayé de résumer ce qui était demandé lors de l'édition site-java-
[Rubiy] J'ai essayé de résumer le traitement de la boucle ce soir [fois, pause ...]
Conférence spéciale sur la simulation multi-échelles: j'ai essayé de résumer le 5e
Conférence spéciale sur la simulation multi-échelles: j'ai essayé de résumer le 8
J'ai essayé de résumer les méthodes de Java String et StringBuilder
Conférence spéciale sur la simulation multi-échelles: j'ai essayé de résumer le 7
[Rails] J'ai essayé de faire passer la version de Rails de 5.0 à 5.2
J'ai essayé d'organiser la session en Rails
J'ai essayé de configurer tomcat pour exécuter le servlet.
J'ai essayé de résumer les points clés de la conception et du développement de gRPC
[Introduction à Java] J'ai essayé de résumer les connaissances que j'estime essentielles
[Ruby] J'ai essayé de résumer les méthodes fréquentes dans paiza
[Ruby] J'ai essayé de résumer les méthodes fréquentes avec paiza ②
05. J'ai essayé de supprimer la source de Spring Boot
J'ai essayé de réduire la capacité de Spring Boot
J'ai essayé de résumer à nouveau le projet qui était difficile à première vue
J'ai essayé de dessiner une animation avec l'API Blazor + canvas
[JavaScript] Le cas le plus fort lorsque j'ai essayé de résumer les parties que je ne comprends pas
J'ai essayé le problème FizzBuzz
J'ai essayé node-jt400 (flux SQL)
[Java] Introduction à l'API Stream
J'ai essayé de vérifier yum-cron
J'ai essayé d'implémenter la méthode de division mutuelle d'Eugrid en Java
J'ai essayé de vérifier le fonctionnement de la requête http (Put) avec Talented API Tester
J'ai essayé d'implémenter la fonction similaire par communication asynchrone
[JDBC] J'ai essayé d'accéder à la base de données SQLite3 depuis Java.
Je souhaite utiliser l'API Java 8 DateTime lentement (maintenant)
J'ai essayé de lier le chat avec le serveur de Minecraft avec l'API Discord
J'ai essayé de construire l'environnement petit à petit en utilisant docker
J'ai essayé de résumer les applications et les outils de développement personnellement utiles (outils de développement)
Ce à quoi j'étais accro avec l'API REST Redmine
J'ai essayé de créer un environnement de WSL2 + Docker + VSCode
J'ai essayé de résumer les applications et les outils de développement personnellement utiles (Apps)
Il est maintenant temps de commencer avec l'API Stream
J'étais étrangement accro à l'utilisation de l'API Stream de Java avec Scala
J'ai essayé de valider pour unifier comment écrire des balises de hachage
J'ai essayé de résumer l'orientation de l'objet à ma manière.
J'ai essayé de mâcher C # (indexeur)