Cela fait longtemps que l'interface de fonction est devenue disponible en Java. Je pensais que c'était assez récent, mais c'était il y a cinq ans. Avec une telle interface de fonction et l'API Stream, je pense qu'il y a une différence considérable entre ceux qui disent «facile à comprendre» et ceux qui disent «difficile à comprendre». Je vais l'écrire comme référence pour savoir comment gérer l'API Stream dans une telle situation.
En bref, on a l'impression que les personnes novices dans les interfaces de fonction, les flux et les options optionnelles développent avec Java 8.
Stream # map
et Stream # filter
) avec des références de méthodesort
, implémentez et écrivez java.util.Comparator
Stream # collect
Quoi qu'il en soit, le fait est d'arrêter de tout mettre dans la chaîne de méthodes.
Il existe plusieurs façons de créer un objet fonctionnel.
Définissez un objet fonction qui prend une chaîne et appelle le StringUtils # isEmpty
défini ci-dessus pour renvoyer le résultat.
Function<String, Boolean> emptyChecker1 = new Function<>{
@Override
public Boolean apply(String s) {
return StringUtils.isEmpty(s);
}
}
Function<String, Boolean> emptyChecker2 = s -> StringUtils.isEmpty(s);
Function<String, Boolean> emptyChecker3 = StringUtils::isEmpty;
Il est particulièrement difficile de faire référence à une méthode, et selon que vous écrivez le nom de la classe ou le nom de la variable à gauche de ::
, la méthode à appeler changera, et dans certains cas elle sera confuse.
Je ne peux pas montrer ce code quand j'ai un enfant avec 1 an d'expérience en développement Java (confession).
list.getValues().forEach(value -> {
if (CollectionUtils.isEmpty(value.getPropertyList())) {
return;
}
if (!CollectionUtils.isEmpty(value.getPropertyList())) {
Property property = value.getPropertyList().stream()
.findFirst()
.orElseThrow(RuntimeException::new);
service.insert(value, property);
return;
}
switch (value.getType()) {
case TYPE_1:
value.getProperty1().setAmount(1000);
break;
case TYPE_2:
value.getProperty2().setAmount(1000);
break;
case TYPE_3:
value.getProperty3().setAmount(1000);
break;
}
service.insert(value);
});
J'essaie de lire le code source en sachant que ** il y a un paragraphe pour chaque bloc de traitement **, mais si l'argument de forEach
est si long, je ne peux pas tout lire en même temps, ce qui me rend mal à l'aise. Je vais. Je suis en troisième année, donc je suis sûr que les enfants en première année ...
Si vous voulez juste le convertir en liste, vous pouvez utiliser Collectors.toList ()
pour vous en débarrasser en un instant, mais je trouve assez difficile d'écrire un processus simple pour convertir une liste en carte. Que faire en cas de duplication? Pour les débutants, les obstacles sont élevés et l'écriture est difficile.
Map<Key, List<Value>> map = values.stream()
.collect(Collectors.groupingBy(Value::getKey));
Nous avons formulé certaines règles en partant du principe que de nombreux membres inexpérimentés sont inscrits.
Le but est de garder les arguments des opérations intermédiaires Stream # map
et Stream # filter
simples et améliorer la lisibilité. Nous pensons que cette règle présente les avantages suivants:
nom de classe (nom de variable) :: nom de méthode
, il est donc facile de comprendre ce que vous faitesLe troisième est un peu difficile à comprendre, je vais donc l'expliquer à l'aide d'un exemple de programme qui calcule le score moyen en classe de l'examen de mi-session.
À propos, l'examen de mi-session comprend trois matières, le japonais, les mathématiques et l'anglais, et considère la mise en œuvre de ʻExaminationScoreSummary # average`.
public class ExaminationScore {
private final Integer japaneseScore;
private final Integer mathScore;
private final Integer englishScore;
// constractor, getter
}
public class ExaminationScoreSummary() {
private final List<ExaminationScore> values;
// constractor, getter
public Integer average() {
// TODO
}
}
Il peut être mis en œuvre de n'importe quelle manière. Je l'écris comme si je l'écrivais toujours.
public class ExaminationScoreSummary() {
private final List<ExaminationScore> values;
// constractor, getter
public Integer average() {
return values.stream()
.mapToInt(score -> score.getJapaneseScore() + score.getMathScore() + score.getEnglishScore())
.average();
}
}
Non, c'est bien, mais lorsque vous demandez le score total, vous devez ajouter les points à l'appelant à chaque fois.
Dans le cas de la référence de méthode, il est impossible pour l'appelant d'ajouter, donc pour le moment, écrivez le processus d'ajout dans la classe ʻExaminationScore`.
public class ExaminationScore {
private final Integer japaneseScore;
private final Integer mathScore;
private final Integer englishScore;
// constractor, getter
public Integer getTotalScore() {
return japaneseScore + mathScore + englishScore;
}
}
Pour les personnes expérimentées, il peut être naturel d'écrire le calcul entre des champs de la même classe dans la méthode dans laquelle les champs sont définis pour augmenter la cohésion. Mais à mon niveau, c'est difficile. C'est une histoire que si vous réglez l'utilisation des références de méthode, vous apprendrez également l'idée de base de la programmation orientée objet.
L'implémentation de l'appelant ressemble à ceci.
public class ExaminationScoreSummary() {
private final List<ExaminationScore> values;
// constractor, getter
public Integer average() {
return values.stream()
.mapToInt(ExaminationScore::getTotalScore)
.average();
}
}
java.util.Comparator
pour trier avec plusieurs clésSupposons que vous souhaitiez publier dans l'ordre décroissant des résultats des examens de mi-session. Non, il y a un problème comme l'exposition de la partition ... Puisqu'il y a 3 matières, elles sont triées par ordre décroissant des scores de langue nationale, et si les scores de langue nationale sont les mêmes, ils sont triés par ordre décroissant des scores de mathématiques.
En utilisant Comparator # compare
et Comparator # thenComparing
, il est possible de mettre en œuvre comme suit.
List<ExaminationScore> values = new ArrayList<>();
values
.stream()
.sorted(Comparator.comparing(ExaminationScore::getJapaneseScore).thenComparing(ExaminationScore::getMathScore())
.collect(Collectors.toList());
Il est pratique de pouvoir trier les choses si facilement. Au fait, si l'ordre de tri devient compliqué, est-ce que j'écrirai tout ici? Non, non, c'est assez difficile. Si vous allez au collège et que vous avez 5 matières ou si vous avez un examen de matières pratiques, ce sera un code très long.
Voici deux façons d'écrire la définition de «dans quel ordre» ailleurs.
C'est un moyen de définir comment trier le type d'élément de collection. Tout d'abord, définissez comment trier. Il n'y a que deux étapes ci-dessous.
public int compareTo (element class o)
dans la classe elementCette fois, les scores sont triés dans l'ordre du score japonais, du score mathématique et du score anglais, donc je l'ai implémenté comme suit.
// 1.Dans la déclaration de classe de l'élément`implements Comparable<Classe d'élément>`Ajoutée
public class ExaminationScore implements Comparable<ExaminationScore> {
private final Integer japaneseScore;
private final Integer mathScore;
private final Integer englishScore;
// constractor, getter
// 2.Dans la classe d'élément`public int compareTo(Classe d'élément o)`Mis en œuvre
public int compareTo(ExaminationScore o) {
if (japaneseScore.compareTo(o.japaneseScore) != 0) {
return japaneseScore.compareTo(o.japaneseScore);
}
if (mathScore.compareTo(o.mathScore) != 0) {
return mathScore.compareTo(o.mathScore);
}
return englishScore.compareTo(o.englishScore);
}
}
J'oublie ce qu'il faut renvoyer avec la valeur de retour de Comparable # compareTo
, donc j'essaye de l'implémenter aussi simple que possible en renvoyant le résultat de la comparaison des variables utilisées comme clé de tri.
Le tri se fait comme suit. Puisqu'il trie par ordre décroissant de score, Comparator # reverseOrder ()
est appelé.
List<ExaminationScore> values = new ArrayList<>();
values
.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
L'argument Stream # sorted
peut être omis en implémentant la méthode Comparable # compareTo
comme" -1 si le score est élevé ", mais je l'ai évité car il est difficile à comprendre.
Contrairement à 1, l'ordre de tri est défini dans une autre classe. La procédure est la suivante:
Comparator <element class>
public int compare (élément classe o1, élément classe o2)
class ExaminationScoreComparator implements Comparator<ExaminationScore> {
@Override
public int compare(ExaminationScore o1, ExaminationScore o2) {
if (Integer.compare(o1.getJapaneseScore(), o2.getJapaneseScore()) != 0) {
return Integer.compare(o1.getJapaneseScore(), o2.getJapaneseScore());
}
if (Integer.compare(o1.getMathScore(), o2.getMathScore()) != 0) {
return Integer.compare(o1.getMathScore(), o2.getMathScore());
}
return Integer.compare(o1.getEnglishScore(), o2.getEnglishScore());
}
}
Le tri se fait comme suit. Passez l'instance de Comparator définie ci-dessus dans l'argument de Stream # sorted
.
List<ExaminationScore> values = new ArrayList<>();
values
.stream()
.sorted(new ExaminationScoreComparator().reverseOrder())
.collect(Collectors.toList());
Comparable vs Comparator
Après tout, il s'agit de savoir lequel utiliser, mais implémentons-le en utilisant Comparator
.
Pour un ordre naturel, la cohérence avec les égaux n'est pas requise, mais fortement recommandée. En effet, l'utilisation d'un ensemble de tri ou d'une mappe de tri qui ne spécifie pas de comparateur explicite avec des éléments ou des clés dont l'ordre naturel est incohérent avec égaux ne garantit pas le comportement de l'ensemble et de la carte. En particulier, de tels ensembles de tri ou cartes de tri enfreignent les règles générales des ensembles ou des cartes. Cette convention est définie en utilisant les termes de la méthode equals.
Comme indiqué dans la documentation officielle, s'il y a un conflit entre ʻequals et
compareTo, le fonctionnement avec
Mapetc. ne sera pas garanti. Si la classe qui implémente
Comparable est la clé
Map,
Map # get utilise le résultat de
compareTo au lieu de la clé ʻequals
, donc vous aurez un bogue étrange.
La même chose a été écrite dans Quel comparateur ou comparateur Java utiliser.
Stream # collect
est coupé ailleursJe pense qu'il est courant de prendre un champ spécifique dans un élément de collection pour créer une nouvelle collection, ou d'utiliser un champ spécifique dans une collection comme clé pour créer une carte. Je pense qu'il vaut mieux pouvoir utiliser ce genre de chose sans toucher à l'API Stream un par un.
/**
*Créez une autre instance à partir de l'élément de liste et renvoyez la liste des instances créées.<br>
*La logique de création d'instance suit l'objet fonction donné dans le deuxième argument.<br>
* @liste de paramètres
* @générateur de paramètres Un objet fonction qui crée une instance d'un autre type à partir d'un élément de la liste
* @param <S>Le type de l'élément de liste d'origine.
* @param <R>Nouveau type d'élément de liste.
* @return Liste des instances générées
*/
public static <S, R> List<Property> collect(List<S> list, Function<S, R> extractor){
return list.stream()
.map(extractor)
.collect(Collectors.toList());
}
Depuis PropertyCollector.java
/**
*Groupez la liste avec une clé spécifique et renvoyez une carte avec la clé et la liste associées.<br>
*La logique de génération de clé suit l'objet de fonction donné dans le deuxième argument.<br>
* @liste de paramètres Liste à grouper
* @param keyExtractor Un objet fonction qui obtient une clé de liste à partir d'un élément de liste
* @param <K>Type de clé. Doit être une classe qui implémente l'interface Comparable
* @param <V>Type d'élément de liste
* @retourner le résultat du regroupement
*/
public static <K, V> Map<K, List<V>> groupingBy(List<V> list, Function<V, K> keyExtractor) {
return list.stream().collect(Collectors.groupingBy(keyExtractor));
}
Depuis MapCollector.java
L'interface de fonction et l'API Stream sont très utiles, mais pour les équipes avec de nombreux membres inexpérimentés, le code source peut devenir moins lisible et moins productif. Dans cet article, j'ai beaucoup écrit sur la blague à laquelle j'ai pensé la troisième année, mais il semble que l'équipe ait également imaginé une manière d'écrire de la même manière et utilisé de nouvelles fonctionnalités Java pour augmenter la productivité de l'équipe. Je serais heureux s'il y en avait.
Recommended Posts