Es ist lange her, dass die Funktionsoberfläche in Java verfügbar wurde. Ich dachte, es sei ziemlich neu, aber es war vor fünf Jahren. Bei einer solchen Funktionsschnittstelle und Stream-API gibt es meines Erachtens einen erheblichen Unterschied zwischen denen, die "leicht zu verstehen" und denen, die "schwer zu verstehen" sagen. Ich werde es als Referenz für den Umgang mit der Stream-API in einer solchen Situation schreiben.
Kurz gesagt, es scheint, als würden Leute, die noch keine Erfahrung mit Funktionsschnittstellen, Streams und Optional haben, mit Java 8 entwickeln.
Stream # map
und Stream # filter
) mit MethodenreferenzenDer Punkt ist jedenfalls, dass wir aufhören, alles in die Methodenkette zu stopfen.
Es gibt verschiedene Möglichkeiten, ein Funktionsobjekt zu erstellen. Definieren Sie ein Funktionsobjekt, das eine Zeichenfolge verwendet und die oben definierten "StringUtils # isEmpty" aufruft, um das Ergebnis zurückzugeben.
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;
Es ist besonders schwierig, auf eine Methode zu verweisen, und je nachdem, ob Sie den Namen der Klasse oder den Namen der Variablen links von ::
schreiben, ändert sich die aufzurufende Methode und ist in einigen Fällen verwirrt.
Ich kann diesen Code nicht anzeigen, wenn ich ein Kind mit 1 Jahr Erfahrung in der Java-Entwicklung habe (Geständnis).
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);
});
Ich versuche, den Quellcode mit dem Bewusstsein zu lesen, dass es für jeden Verarbeitungsblock einen Absatz gibt, aber wenn das Argument von "forEach" so lang ist, kann ich nicht alles auf einmal lesen, was mich unbehaglich macht. Ich werde. Ich bin im dritten Jahr, also bin ich sicher, dass die Kinder im ersten Jahr ...
Wenn Sie nur in List konvertieren möchten, können Sie "Collectors.toList ()" verwenden, um es sofort zu entfernen, aber ich finde es ziemlich schwierig, einen einfachen Prozess zum Konvertieren von List in Map zu schreiben. Was tun bei Doppelarbeit? Für Anfänger sind die Hürden hoch und das Schreiben ist mühsam.
Map<Key, List<Value>> map = values.stream()
.collect(Collectors.groupingBy(Value::getKey));
Wir haben einige Regeln unter der Annahme formuliert, dass viele unerfahrene Mitglieder eingeschrieben sind.
Der Zweck besteht darin, die Argumente der Zwischenoperationen "Stream # map" und "Stream # filter" einfach zu halten und die Lesbarkeit zu verbessern. Wir glauben, dass diese Regel die folgenden Vorteile hat:
Das dritte ist etwas schwer zu verstehen, daher werde ich es anhand eines Beispiels eines Programms erläutern, das die durchschnittliche Punktzahl der Halbzeitprüfung in der Klasse berechnet.
Übrigens, die Halbzeitprüfung setzt drei Fächer voraus, Japanisch, Mathematik und Englisch, und berücksichtigt die Implementierung von "ExaminationScoreSummary # durchschnitt".
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
}
}
Es kann auf jede Weise implementiert werden. Ich schreibe es, als ob ich es immer schreibe.
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();
}
}
Nein, das ist in Ordnung, aber wenn Sie nach der Gesamtpunktzahl fragen, sollten Sie die Punkte jedes Mal beim Anrufer hinzufügen.
Im Fall einer Methodenreferenz kann der Aufrufer nichts hinzufügen. Schreiben Sie daher vorerst den Prozess des Hinzufügens in die Klasse "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;
}
}
Für erfahrene Personen kann es natürlich sein, die Berechnung zwischen Feldern derselben Klasse in der Methode zu schreiben, in der die Felder definiert werden, um die Kohäsivität zu erhöhen. Aber auf meiner Ebene ist es schwierig. Es ist eine Geschichte, dass Sie, wenn Sie die Verwendung von Methodenreferenzen regieren, auch die Grundidee der objektorientierten Programmierung lernen.
Die Implementierung des Anrufers sieht folgendermaßen aus.
public class ExaminationScoreSummary() {
private final List<ExaminationScore> values;
// constractor, getter
public Integer average() {
return values.stream()
.mapToInt(ExaminationScore::getTotalScore)
.average();
}
}
java.util.Comparator
, um mit mehreren Schlüsseln zu sortierenAngenommen, Sie möchten in absteigender Reihenfolge der Prüfungsergebnisse zur Halbzeit posten. Nein, es gibt ein Problem wie das Belichten der Partitur ... Da es 3 Fächer gibt, werden sie in absteigender Reihenfolge der nationalen Sprachnoten sortiert. Wenn die nationalen Sprachnoten gleich sind, werden sie in absteigender Reihenfolge der mathematischen Noten sortiert.
Mit Comparator # compare
und Comparator # thenComparing
ist es möglich, Folgendes zu implementieren.
List<ExaminationScore> values = new ArrayList<>();
values
.stream()
.sorted(Comparator.comparing(ExaminationScore::getJapaneseScore).thenComparing(ExaminationScore::getMathScore())
.collect(Collectors.toList());
Es ist praktisch, Dinge so einfach sortieren zu können. Übrigens, wenn die Sortierreihenfolge kompliziert wird, schreibe ich hier alles? Nein, nein, es ist ziemlich hart. Wenn Sie zur Junior High School gehen und 5 Fächer haben oder wenn Sie eine Prüfung für praktische Fächer haben, wird es ein sehr langer Code sein.
Hier sind zwei Möglichkeiten, die Definition von "in welcher Reihenfolge" an anderer Stelle zu schreiben.
Auf diese Weise können Sie festlegen, wie nach dem Typ des Auflistungselements sortiert werden soll. Definieren Sie zunächst, wie sortiert werden soll. Es gibt nur zwei Schritte unten.
implementiert Comparable <Elementklasse>
hinzuDieses Mal sind die Punktzahlen in der Reihenfolge der japanischen Punktzahl, der mathematischen Punktzahl und der englischen Punktzahl sortiert, daher habe ich sie wie folgt implementiert.
// 1.In der Klassendeklaration des Elements`implements Comparable<Elementklasse>`Hinzugefügt
public class ExaminationScore implements Comparable<ExaminationScore> {
private final Integer japaneseScore;
private final Integer mathScore;
private final Integer englishScore;
// constractor, getter
// 2.In der Elementklasse`public int compareTo(Elementklasse o)`Implementiert
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);
}
}
Ich habe vergessen, was mit dem Rückgabewert "Comparable # compareTo" zurückgegeben werden soll, und versuche daher, dies so einfach wie möglich zu implementieren, indem ich das Vergleichsergebnis der Variablen zurückgebe, die der Schlüssel zum Sortieren sind.
Die Sortierung erfolgt wie folgt. Da es in absteigender Reihenfolge der Punktzahl sortiert wird, wird "Comparator # reverseOrder ()" aufgerufen.
List<ExaminationScore> values = new ArrayList<>();
values
.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
Das Argument "Stream # sortiert" kann weggelassen werden, indem die Methode "Comparable # compareTo" als "-1, wenn die Punktzahl hoch ist" implementiert wird. Ich habe es jedoch vermieden, da es schwer zu verstehen ist.
Im Gegensatz zu 1 wird die Sortierreihenfolge in einer anderen Klasse definiert. Das Verfahren ist wie folgt:
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());
}
}
Die Sortierung erfolgt wie folgt. Übergeben Sie die oben im Argument "Stream # sortiert" definierte Instanz von Comparator.
List<ExaminationScore> values = new ArrayList<>();
values
.stream()
.sorted(new ExaminationScoreComparator().reverseOrder())
.collect(Collectors.toList());
Comparable vs Comparator
Immerhin ist es eine Frage der zu verwendenden, aber im Grunde wollen wir es mit Comparator
implementieren.
Für eine natürliche Bestellung ist keine Übereinstimmung mit Gleichen erforderlich, wird jedoch dringend empfohlen. Dies liegt daran, dass die Verwendung eines Sortiersatzes oder einer Sortierzuordnung, die keinen expliziten Komparator mit Elementen oder Schlüsseln angibt, deren natürliche Reihenfolge nicht mit gleich übereinstimmt, das Verhalten des Satzes und der Zuordnung nicht garantiert. Insbesondere verletzen solche Sortiersätze oder Sortierkarten die allgemeinen Regeln von Sätzen oder Karten. Diese Konvention wird unter Verwendung der Begriffe der Gleichheitsmethode definiert.
Wie in der offiziellen Dokumentation angegeben, kann bei einem Konflikt zwischen "gleich" und "compareTo" der Betrieb mit "Karte" usw. nicht garantiert werden. Wenn die Klasse, die "Comparable" implementiert, der Schlüssel "Map" ist, verwendet "Map # get" das Ergebnis von "compareTo" anstelle des Schlüssels "equals", sodass Sie einen seltsamen Fehler haben.
Dasselbe wurde in Welcher Java-Vergleicher oder Komparator verwendet werden soll geschrieben.
Ich denke, es ist üblich, ein bestimmtes Feld in einem Sammlungselement zu verwenden, um eine neue Sammlung zu erstellen, oder ein bestimmtes Feld in einer Sammlung als Schlüssel zum Erstellen einer Karte zu verwenden. Ich denke, es ist besser, solche Dinge verwenden zu können, ohne die Stream-API einzeln zu berühren.
/**
*Erstellen Sie eine weitere Instanz aus dem Listenelement und geben Sie die Liste der erstellten Instanzen zurück.<br>
*Die Instanzerstellungslogik folgt dem im zweiten Argument angegebenen Funktionsobjekt.<br>
* @Parameterliste Liste
* @Parametergenerator Ein Funktionsobjekt, das aus einem Element in der Liste eine Instanz eines anderen Typs erstellt
* @param <S>Der Typ des ursprünglichen Listenelements.
* @param <R>Neuer Listenelementtyp.
* @return Liste der generierten Instanzen
*/
public static <S, R> List<Property> collect(List<S> list, Function<S, R> extractor){
return list.stream()
.map(extractor)
.collect(Collectors.toList());
}
/**
*Gruppieren Sie die Liste mit einem bestimmten Schlüssel und geben Sie eine Karte mit dem gepaarten Schlüssel und der Liste zurück.<br>
*Die Schlüsselgenerierungslogik folgt dem im zweiten Argument angegebenen Funktionsobjekt.<br>
* @Parameterliste Zu gruppierende Liste
* @param keyExtractor Ein Funktionsobjekt, das einen Listenschlüssel von einem Listenelement erhält
* @param <K>Schlüsselart. Muss eine Klasse sein, die die Schnittstelle Comparable implementiert
* @param <V>Elementtyp auflisten
* @Gruppierungsergebnis zurückgeben
*/
public static <K, V> Map<K, List<V>> groupingBy(List<V> list, Function<V, K> keyExtractor) {
return list.stream().collect(Collectors.groupingBy(keyExtractor));
}
Die Funktionsoberfläche und die Stream-API sind sehr nützlich, aber für Teams mit vielen unerfahrenen Mitgliedern kann der Quellcode weniger lesbar und weniger produktiv sein. In diesem Artikel habe ich viel über den Witz geschrieben, über den ich im dritten Jahr nachgedacht habe, aber es scheint, dass das Team auch eine Schreibweise entwickelt und neue Java-Funktionen verwendet hat, um die Produktivität des Teams zu steigern. Ich würde mich freuen, wenn es so wäre.
Recommended Posts