Essayez différentes méthodes d'API Java Stream (maintenant)

API Stream ajoutée dans Java 8. J'ai fait des recherches et utilisé le minimum au besoin, mais je ne l'ai pas bien compris, alors j'ai réexaminé et essayé.

Qu'est-ce que l'API Stream en premier lieu?

"Une classe qui prend en charge les opérations fonctionnelles sur un flux d'éléments". https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/package-summary.html … Le document officiel est difficile à lire, probablement parce qu'il s'agit d'une traduction automatique. Vous pouvez comprendre qui a lu l'anglais ...

L '"élément" semble ici se référer principalement à des éléments de collection tels que List. Stream est exactement comme il est écrit dans cet article. https://qiita.com/castaneai/items/3dbdd8543b020aa903cb

À quoi sert StreamAPI?

Mon interprétation est

--Facile à utiliser sur les éléments de la collection

Présentation de l'API Stream

J'ai senti que JavaDoc pour l'interface Stream serait la base de tout, donc je vais me concentrer ici. https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html

Flux de processus

  1. Créez un flux à partir d'une source (telle qu'une liste)
  2. 0 à n opérations intermédiaires
  3. Une opération de terminaison

スクリーンショット 2018-12-05 6.06.52.png

Point (extrait de JavaDoc)

C'est une expérience pour le moment!

Les éléments dérivés tels que les méthodes qui renvoient IntStream sont exclus.

Avant ça

J'ai créé un bean et une méthode pour créer des données de test. Je l'appelle à partir de la méthode principale, crée une variable appelée liste, puis expérimente.

Bean

Bean


public class TestBean {
	String key;
	String value;
	String description;
	public TestBean(String key, String value, String description) {
		this.setKey(key);
		this.setValue(value);
		this.setDescription(description);
	}
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	@Override
	public String toString() {
		return "key:" + this.key + " value:" + this.value + " description:" + this.description;
	}
}
méthode

Méthode de création des données de test


	private static List<TestBean> editTestData() {
		List<TestBean> list = new ArrayList<>();
		list.add(new TestBean("B", "Butaman", "Contient du porc"));
		list.add(new TestBean("A", "Anman", "Contient anko"));
		list.add(new TestBean("C", "Chukaman", "C'est un terme général pour Anmanbutaman etc."));
		return list;
	}

Fonctionnement intermédiaire

filter Comme son nom l'indique, il a filtré!

filter


list.stream()
.filter(x -> x.getValue().equals("Anman"))
.forEach(x -> System.out.println(x.getDescription()));

Résultat d'exécution


Contient anko

map Je l'ai interprété comme la création d'un autre Stream basé sur le Stream. Dans cet exemple, un Stream configuré avec le type TestBean est remplacé par un Stream configuré avec une String.

map


list.stream()
.map(x -> x.getKey() + x.getValue() + x.getDescription())
.forEach(x -> System.out.println(x));

Résultat d'exécution


B Butaman contient du porc
Un Anman Anko est inclus
C Chukaman Anmanbutaman etc.

flatMap Il semble que vous puissiez créer un Stream dans un Stream. Une image comme faire un tableau avec Stream? Ici, chaque élément du Bean qui constitue une ligne est séparé pour créer un autre Stream.

flatMap


list.stream()
.flatMap(x -> Stream.of(Arrays.asList(x.getKey(),x.getValue(),x.getDescription())))
.forEach(x -> x.forEach(y -> System.out.println(y)));

Résultat d'exécution


B
Butaman
Contient du porc
A
Anman
Contient anko
C
Chukaman
C'est un terme général pour Anmanbutaman etc.

distinct Éliminez les éléments en double. En effectuant ce test, nous avons gonflé les données de test ci-dessus.

<détails>

Données de test </ summary>

Données de test (dupliquer a)


	private static List<TestBean> editTestData() {
		List<TestBean> list = new ArrayList<>();
		TestBean a = new TestBean("A", "Anman", "Contient anko");
		TestBean b = new TestBean("B", "Butaman", "Contient du porc");
		TestBean c = new TestBean("C", "Chukaman", "C'est un terme général pour Anmanbutaman etc.");
		list.add(b);
		list.add(a);
		list.add(a);
		list.add(a);
		list.add(c);
		return list;
	}

<détails>

Pas distinct </ summary>

La source


list.stream()
.forEach(x -> System.out.println(x.getDescription()));

Résultat d'exécution


Contient du porc
Contient anko
Contient anko
Contient anko
C'est un terme général pour Anmanbutaman etc.

<détails> <résumé> avec </ résumé>

distinct

La source


list.stream()
.distinct()
.forEach(x -> System.out.println(x.getDescription()));

Résultat d'exécution


Contient du porc
Contient anko
C'est un terme général pour Anmanbutaman etc.

sorted Trier comme son nom l'indique. Dans cet exemple, les éléments nommés key (ABC) sont triés dans l'ordre.

sorted


list.stream()
.sorted((o1, o2) -> o1.getKey().compareTo(o2.getKey()))
.forEach(x -> System.out.println(x.getKey() + x.getValue() + x.getDescription()));

Résultat d'exécution


Un Anman Anko est inclus
B Butaman contient du porc
C Chukaman Anmanbutaman etc.

peek Cela ne manipule pas spécifiquement le contenu du flux, Cela ressemble à une méthode de débogage. peek = Un aperçu (traduit littéralement)

peek


list.stream()
.peek(x -> System.out.println(x))
.forEach(x -> System.out.println("Sortie avec forEach"));

Résultat d'exécution


key:B value:Description du butaman:Contient du porc
Sortie avec forEach
key:A value:Description d'Anman:Contient anko
Sortie avec forEach
key:C value:Description de Chukaman:C'est un terme général pour Anmanbutaman etc.
Sortie avec forEach

Je me demandais si forEach serait fait après que tous les aperçus aient été faits en premier, mais il semble que le traitement intermédiaire → le traitement de fin soit fait pour chaque élément.

limit Tronquez le nombre de flux au nombre spécifié.

limit


list.stream()
.limit(2)
.forEach(x -> System.out.println(x));

Résultat d'exécution


key:B value:Description du butaman:Contient du porc
key:A value:Description d'Anman:Contient anko

skip Contrairement à la limite, il saute le début de Stream.

skip


list.stream()
.skip(1)
.forEach(x -> System.out.println(x));

Résultat d'exécution


key:A value:Description d'Anman:Contient anko
key:C value:Description de Chukaman:C'est un terme général pour Anmanbutaman etc.

Opération de terminaison

forEach Je l'ai utilisé avec désinvolture à chaque fois jusqu'à présent. Traite tous les éléments de Stream. … Mais il semble que la commande ne soit pas garantie. Uniquement pour les flux parallèles?

forEachOrdered C'est la même chose que forEach, mais il semble que la commande soit garantie en utilisant ceci.

toArray Faites simplement du flux un tableau.

toArray


Object[] testBeans = list.stream().toArray();
System.out.println(testBeans[0].toString());
System.out.println(testBeans[1].toString());

Résultat d'exécution


key:B value:Description du butaman:Contient du porc
key:A value:Description d'Anman:Contient anko

reduce réduire = réduire, réduire (littéralement) Cela semble utile lors de l'agrégation du contenu de Stream. Dans cet exemple, l'élément nommé value est attaché. Un seul facultatif est renvoyé par Stream. En outre, il existe également une réduction qui peut définir la valeur initiale. Différence de signature.

reduce


Optional<TestBean> opt = list.stream()
.reduce((t, u) -> {
  t.setValue(t.getValue() + u.getValue());
  return t;
});
System.out.println(opt.orElse(new TestBean("x", "x", "x")));

Résultat d'exécution


key:B value:Description de Butaman Anman Chukaman:Contient du porc

collect Il semble être utilisé lorsque vous souhaitez le renvoyer en tant que collection au lieu d'un seul élément comme facultatif. Collection → Modifier en tant que flux → Sortie en tant que collection Je pense que je l'utilise assez souvent.

Si vous souhaitez implémenter sérieusement les différentes interfaces qui sont les arguments de collect par vous-même, vous devez écrire environ 5 méthodes abstraites, il est donc facile d'utiliser la méthode statique fournie dans la classe Collectors.

collect


List<TestBean> newlist = list.stream()
  .filter(x -> x.getKey().equals("C"))
  .collect(Collectors.toList());
System.out.println(newlist.get(0).toString());

Résultat d'exécution


key:C value:Description de Chukaman:C'est un terme général pour Anmanbutaman etc.

min Comme son nom l'indique, il renvoie le plus petit comme facultatif. Le minimum est le même que pour le tri, et le comparateur est implémenté et défini. Dans cet exemple, spécifiez l'élément appelé key.

min


Optional<TestBean> op = list.stream()
  .min((x, y) -> x.getKey().compareTo(y.getKey()));
System.out.println(op.orElse(new TestBean("x", "x", "x")).toString());

Résultat d'exécution


key:A value:Description d'Anman:Contient anko

max Le contraire de min!

max


Optional<TestBean> op = list.stream()
  .max((x, y) -> x.getKey().compareTo(y.getKey()));
System.out.println(op.orElse(new TestBean("x", "x", "x")).toString());

Résultat d'exécution


key:C value:Description de Chukaman:C'est un terme général pour Anmanbutaman etc.

count Comme son nom l'indique, il renvoie le nombre d'éléments Stream.

count


System.out.println(list.stream().count());

Résultat d'exécution


3

anyMatch Comme je l'ai lu, il renvoie vrai si l'une des conditions de Stream est remplie. Dans cet exemple, nous recherchons un élément avec la clé "A".

anyMatch


boolean matched = list.stream().anyMatch(x -> x.getKey().equals("A"));
if (matched) {
  System.out.println("Il y avait un homme!");
}

Résultat d'exécution


Il y avait un homme!

allMatch Tant que vous ne lirez pas ceci également, il renvoie true si tous les éléments du Stream remplissent les conditions spécifiées.

allMatch


boolean matched = list.stream().allMatch(x -> !x.getKey().equals("Z"));
if (matched) {
  System.out.println("Tout le monde n'est pas Z!");
}

Résultat d'exécution


Tout le monde n'est pas Z!

noneMatch Renvoie true s'il n'y a rien de tel dans le Stream.

noneMatch


boolean noMatched = list.stream().noneMatch(x -> x.getKey().equals("Z"));
if (noMatched) {
  System.out.println("Il n'y avait pas de guerrier Z");
}

Résultat d'exécution


Il n'y avait pas de guerrier Z

findFirst Comme son nom l'indique, il renvoie le premier!

findFirst


Optional<TestBean> op = list.stream().findFirst();
System.out.println(op.orElse(new TestBean("x", "x", "x")).toString());

Résultat d'exécution


key:B value:Description du butaman:Contient du porc

findAny Il passe par son nom et renvoie quelque chose. Je parle de quelque chose ... Je ne sais pas vraiment ce qui sera retourné. Vous ne pouvez pas spécifier d'argument. Quand je l'ai essayé avec un flux séquentiel, le premier élément a été renvoyé, mais il semble qu'il n'y ait aucune garantie. (2018/12/08 j'ai reçu un commentaire et l'ai corrigé)

findAny


Optional<TestBean> op = list.stream().findAny();
System.out.println(op.orElse(new TestBean("x", "x", "x")).toString());

Résultat d'exécution


key:B value:Description du butaman:Contient du porc

Expériences autres que la méthode

Vérifiez que la source d'origine n'a pas changé même si vous utilisez Stream

J'ai écrasé l'élément de valeur du bean dans le flux avec une chaîne fixe. La source (List) n'a certainement pas changé.

<détails>

source </ summary>

python


System.out.println("【Changer avant】");
for (TestBean bean : list) {
  System.out.println(bean);
}

list.stream().map(x -> {
  x.setValue("Je l'ai changé!");
  return x;
});

System.out.println("[Après le changement]");
for (TestBean bean : list) {
  System.out.println(bean);
}

<détails>

Résultat de l'exécution </ summary>

Résultat d'exécution


【Changer avant】
key:B value:Description du butaman:Contient du porc
key:A value:Description d'Anman:Contient anko
key:C value:Description de Chukaman:C'est un terme général pour Anmanbutaman etc.
[Après le changement]
key:B value:Description du butaman:Contient du porc
key:A value:Description d'Anman:Contient anko
key:C value:Description de Chukaman:C'est un terme général pour Anmanbutaman etc.

Mesurer la différence de vitesse

Créer une liste de 150 millions Stockez des nombres aléatoires de 0 à 99 dedans Créez une autre liste en extrayant uniquement les éléments qui stockent 99 J'ai fait un processus inutile et je l'ai mesuré.

En conséquence, il n'y a pas de grande différence entre List et stream, paralellStream était évidemment rapide.

<détails>

source </ summary>

python


    final int loopsize = 150000000;
    final int randomrange = 100;
    final int findnum = 99;

    // create many data
    System.out.println("Pendant la génération des données de test...");
    List<Integer> list = IntStream.range(0, loopsize)
        .mapToObj(x -> new Random().nextInt(randomrange))
        .collect(Collectors.toList());

    // byList
    LocalDateTime starttime = start("--- byList ---");
    List<Integer> newlist = new ArrayList<>();
    for (Integer i : list) {
      if (i.intValue() == findnum) {
        newlist.add(i);
      }
    }
    end(starttime, findnum, newlist);

    // byStream
    starttime = start("--- byStream ---");
    List<Integer> streamlist = list.stream()
        .filter(x -> x.intValue() == findnum).collect(Collectors.toList());
    end(starttime, findnum, streamlist);

    // byParalellStream
    starttime = start("--- byParalellStream ---");
    List<Integer> paralellStreamlist = list.parallelStream()
        .filter(x -> x.intValue() == findnum).collect(Collectors.toList());
    end(starttime, findnum, paralellStreamlist);
  }

  private static LocalDateTime start(String msg) {
    System.out.println("");
    System.out.println(msg);
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println("start: " + ldt);
    return ldt;
  }

  private static void end(LocalDateTime starttime, int findnum,
      List<Integer> newlist) {
    LocalDateTime elapsed = LocalDateTime.now();
    System.out.println("end  : " + elapsed);
    elapsed = elapsed.minusHours(starttime.getHour());
    elapsed = elapsed.minusMinutes(starttime.getMinute());
    elapsed = elapsed.minusSeconds(starttime.getSecond());
    elapsed = elapsed.minusNanos(starttime.getNano());
    System.out
        .println("elapsed: " + elapsed.format(DateTimeFormatter.ISO_TIME));
    System.out.println(findnum + "Est" + newlist.size() + "Il y en avait un!");
  }

<détails>

Résultat de l'exécution </ summary>

Résultat d'exécution


Pendant la génération des données de test...

--- byList ---
start: 2018-12-08T15:35:51.584
end  : 2018-12-08T15:35:52.039
elapsed: 00:00:00.455
Il y en avait 1500072 99!

--- byStream ---
start: 2018-12-08T15:35:52.052
end  : 2018-12-08T15:35:52.450
elapsed: 00:00:00.398
Il y en avait 1500072 99!

--- byParalellStream ---
start: 2018-12-08T15:35:52.450
end  : 2018-12-08T15:35:52.672
elapsed: 00:00:00.222
Il y en avait 1500072 99!

Confirmez que l'ordre de forEach et findAny n'est pas garanti

Ce n'était certainement pas garanti avec parallelStream. Dans Sequential Stream, il a été accédé dans l'ordre de la source d'origine (Liste).

<détails>

source </ summary>

python


    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    System.out.println("--- byList ---");
    for (int i : list) {
      System.out.println(i);
    }

    System.out.println("--- byStream ---");
    list.stream().forEach(i -> System.out.println(i));

    System.out.println("--- byParallelStream ---");
    list.parallelStream().forEach(i -> System.out.println(i));

    System.out
        .println("findany by stream:" + list.stream().findAny().orElse(null));

    System.out.println("findany by parallelStream:"
        + list.parallelStream().findAny().orElse(null));

<détails>

Résultat de l'exécution </ summary>

Résultat d'exécution


--- byList ---
1
2
3
4
5
6
7
8
9
10
--- byStream ---
1
2
3
4
5
6
7
8
9
10
--- byParallelStream ---
7
9
6
3
5
2
10
1
4
8
findany by stream:1
findany by parallelStream:7

Point confus

Arguments pour chaque méthode dans l'interface Stream

Il passe une interface fonctionnelle (comportement) comme argument au lieu de données. C'est ce qu'on appelle la programmation fonctionnelle. Je ne pouvais pas l'écrire facilement parce que je ne l'ai écrit que de manière procédurale.

Je l'oublie bientôt et je ne peux pas l'écrire d'un seul coup, donc au lieu d'utiliser une expression lambda, j'ai créé une classe anonyme avec une nouvelle interface () {...}, implémenté une méthode abstraite et l'ai remplacée par une expression lambda. .. J'ai honte de dire ... Eh bien, vous vous y habituerez bientôt.

Articles que j'ai utilisés comme référence

Merci à tous les auteurs!

Principes de base de l'API Stream https://qiita.com/Takmiy/items/f1d44dfde0d3a906d321

Enquête sur la fonction Java 8, consommateur, fournisseur, prédicat https://qiita.com/subaru44k/items/c55d9b9fc419f0d09c64

Qu'est-ce que la programmation fonctionnelle et qu'est-ce que lambda? https://qiita.com/lrf141/items/98ffbeaee42d30cca4dc

Différence entre Stream map et flatMap https://qiita.com/KevinFQ/items/97137efb2159009b60e1

En dehors de Qiita. Il décrit le but de l'API Stream jusqu'aux résultats expérimentaux. http://d.hatena.ne.jp/nowokay/20130506

Recommended Posts