Java8 nouveau flux de fonctionnalités

Nouvelles fonctionnalités de JDK1.8

1. Introduction

JDK1.8 est sorti depuis longtemps et est déjà utilisé dans de nombreuses entreprises. Et Spring5 et SpringBoot2.0 sont recommandés pour utiliser JDK1.8 ou supérieur. Nous devons donc suivre le rythme du temps et accepter le changement.

Cette version de Jdk8 contient plus de dix nouvelles fonctionnalités dans les langages, les compilateurs, les bibliothèques, les outils et la JVM. Dans cet article, nous allons apprendre de nouvelles fonctionnalités dans :

  • Expression lambda
  • interface fonctionnelle
  • référence de la méthode]
  • Méthodes d'interface par défaut et méthodes statiques
  • Facultatif
  • Ruisseaux
  • réseau parallèle

2. Expressions lambda

programmation fonctionnelle

Les expressions lambda, également appelées fermetures, sont la nouvelle fonctionnalité la plus importante à l'origine de la sortie de Java 8. Lambda permet de transmettre des fonctions en tant que paramètres d'une méthode (les fonctions sont transmises en tant que paramètres dans les méthodes). Cela peut rendre le code plus concis et compact.

2.1 Syntaxe de base :

(参数列表) -> {代码块}

demande attention :

  • Le type de paramètre peut être omis, et le compilateur peut le déduire par lui-même
  • S'il n'y a qu'un seul paramètre, les parenthèses peuvent être omises
  • Si le bloc de code n'est qu'une ligne de code, les accolades peuvent également être omises
  • Si le bloc de code est une ligne et est une expression avec des résultats, returnil peut être omis

Remarque : En fait, considérez les expressions Lambda comme un raccourci pour les classes internes anonymes. Bien entendu, la prémisse est que la classe interne anonyme doit correspondre à une interface, et qu'il ne doit y avoir qu'une seule fonction dans l'interface ! L'expression Lambda consiste à écrire directement des fonctions : liste de paramètres, corps de code, valeur de retour et autres informations,用函数来代替完整的匿名内部类 !

2.2 Exemple d'utilisation

Exemple 1 : plusieurs paramètres

Préparez une collection :

// 准备一个集合
List<Integer> list = Arrays.asList(10, 5, 25, -15, 20);

Supposons que nous voulions trier la collection, regardons d'abord la méthode d'écriture de JDK7, nous devons en construire une via une classe interne anonyme Comparator:

// Jdk1.7写法
Collections.sort(list,new Comparator<Integer>() {
    
    
    @Override
    public int compare(Integer o1, Integer o2) {
    
    
        return o1 - o2;
    }
});
System.out.println(list);// [-15, 5, 10, 20, 25]

S'il s'agit de jdk8, nous pouvons utiliser la nouvelle sort(Comparator c)méthode API de collection : pour recevoir un comparateur, et nous utilisons Lambda pour remplacer Comparatorla classe interne anonyme :

// Jdk1.8写法,参数列表的数据类型可省略:
list.sort((i1,i2) -> {
    
     return i1 - i2;});

System.out.println(list);// [-15, 5, 10, 20, 25]

En comparant la méthode Comparatordans compare(), vous constaterez que l'expression Lambda écrite ici n'est que compare()la forme abrégée de la méthode, et JDK8 la compilera dans une classe interne anonyme. N'est-ce pas beaucoup plus simple !

Ne vous inquiétez pas, nous avons constaté que le bloc de code ici n'a qu'une seule ligne de code, ce qui est conforme aux règles d'omission précédentes. Nous pouvons l'abréger comme suit :

// Jdk8写法
// 因为代码块是一个有返回值的表达式,可以省略大括号以及return
list.sort((i1,i2) -> i1 - i2);
Exemple 2 : Paramètre unique

En prenant la collection tout à l'heure comme exemple, nous voulons maintenant parcourir les éléments de la collection et les imprimer.

Utilisez d'abord la méthode jdk1.7 :

// JDK1.7遍历并打印集合
for (Integer i : list) {
    
    
    System.out.println(i);
}

jdk1.8 ajoute une méthode à la collection : foreach(), qui reçoit une fonction qui opère sur les éléments :

// JDK1.8遍历并打印集合,因为只有一个参数,所以我们可以省略小括号:
list.forEach(i -> System.out.println(i));
Exemple 3 : Attribuer Lambda à une variable

L'essence de l'expression Lambda est en fait une classe interne anonyme, nous pouvons donc attribuer une expression Lambda à une variable.

// 将一个Lambda表达式赋值给某个接口:
Runnable task = () -> {
    
    
    // 这里其实是Runnable接口的匿名内部类,我们在编写run方法。
    System.out.println("hello lambda!");
};
new Thread(task).start();

Cependant, l'utilisation ci-dessus est rare et Lambda est généralement utilisé directement comme paramètre.

Exemple 4 : finale implicite

L'essence de l'expression Lambda est en fait une classe interne anonyme, et lorsqu'une classe interne anonyme accède à une variable locale externe, la variable doit être déclarée comme final! Cependant, nous n'avons pas besoin de déclarer lors de l'utilisation d'expressions Lambda finalCela ne signifie pas que nous enfreignons les règles des classes internes anonymes, car la couche inférieure de Lambda définira implicitement la variable sur final, et la variable ne doit pas être modifiée dans les opérations ultérieures :

Exemple correct :

// 定义一个局部变量
int num = -1;
Runnable r = () -> {
    
    
    // 在Lambda表达式中使用局部变量num,num会被隐式声明为final
    System.out.println(num);
};
new Thread(r).start();// -1

Cas d'erreur :

// 定义一个局部变量
int num = -1;
Runnable r = () -> {
    
    
    // 在Lambda表达式中使用局部变量num,num会被隐式声明为final,不能进行任何修改操作
    System.out.println(num++);
};
new Thread(r).start();//报错

3. Interface fonctionnelle

Après l'étude précédente, je pense que tout le monde a une compréhension préliminaire des expressions Lambda. en conclusion:

  • Les expressions lambda sont un raccourci pour les classes internes anonymes d'interfaces
  • L'interface doit satisfaire : il n'y a qu'une seule fonction à l'intérieur

RunnableEn fait, une telle interface s'appelle une interface fonctionnelle, et ce que nous avons appris Comparatorest un représentant typique d'une interface fonctionnelle. Mais en pratique, l'interface fonctionnelle est très fragile, tant que quelqu'un ajoute une méthode de plus à l'interface, alors l'interface n'est pas une interface fonctionnelle, et la compilation échouera. Java 8 fournit une annotation spéciale @FunctionalInterfacepour surmonter la vulnérabilité mentionnée ci-dessus et indiquer explicitement l'interface fonctionnelle. @FunctionalInterfaceDe plus, dans la version jdk8, des annotations ont été ajoutées à de nombreuses interfaces existantes , telles que Runnablel'interface :

[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-ODT4QdNs-1669729296892)(runnable.png)]

De plus, Jdk8 fournit par défaut certaines interfaces fonctionnelles que nous pouvons utiliser :

3.1 Interface des types de fonctions

@FunctionalInterface
public interface Function<T, R> {
    
    
	// 接收一个参数T,返回一个结果R
    R apply(T t);
}

Function représente une fonction qui a des paramètres et renvoie une valeur. Il existe de nombreuses interfaces Function similaires :

nom de l'interface décrire
BiFunction<T,U,R> Une fonction qui prend deux arguments de type T et U et renvoie un résultat de type R
DoubleFunction<R> Une fonction qui accepte un paramètre de type double et renvoie un résultat de type R
IntFunction<R> Une fonction qui accepte un paramètre de type int et renvoie un résultat de type R
LongFunction<R> Une fonction qui accepte un paramètre de type long et renvoie un résultat de type R
ToDoubleFunction<T> Recevoir des paramètres de type T et renvoyer des résultats de type double
ToIntFunction<T> Recevoir le paramètre de type T et renvoyer le résultat de type int
ToLongFunction<T> Recevoir des paramètres de type T et renvoyer des résultats de type long
DoubleToIntFunction Recevoir un paramètre de type double et renvoyer le résultat de type int
DoubleToLongFunction Recevoir un paramètre de type double et renvoyer un résultat de type long

Voyez-vous le modèle? Il s'agit d'une classe d'interfaces fonctionnelles, dérivée de Function, qui soit spécifient que les paramètres ne déterminent pas le résultat de retour, soit spécifient que le résultat ne connaît pas le type de paramètre, ou les deux.

3.2 Série grand public

@FunctionalInterface
public interface Consumer<T> {
    
    
	// 接收T类型参数,不返回结果
    void accept(T t);
}

Comme la série Function, la série Consumer possède diverses interfaces dérivées, qui ne sont pas répertoriées ici. Cependant, ils ont tous des caractéristiques similaires : c'est-à-dire qu'ils ne renvoient aucun résultat.

3.3 Série de prédicats

@FunctionalInterface
public interface Predicate<T> {
    
    
	// 接收T类型参数,返回boolean类型结果
    boolean test(T t);
}

Les paramètres de la série Predicate ne sont pas fixes, mais le retour doit être de type booléen.

3.4 Série fournisseur

@FunctionalInterface
public interface Supplier<T> {
    
    
	// 无需参数,返回一个T类型结果
    T get();
}

Supplier series, la traduction anglaise est "supplier", comme son nom l'indique : only output, no collection. Par conséquent, il n'accepte aucun paramètre et renvoie un résultat de type T.

4. Référence de la méthode

Les références de méthode permettent aux développeurs de transmettre des méthodes existantes en tant que variables. Les références de méthode peuvent être utilisées avec des expressions Lambda.

4.1 Syntaxe :

Il existe quatre types de références de méthode :

grammaire décrire
nom de classe :: nom de méthode statique Une référence à une méthode statique d'une classe
nom de classe :: nom de méthode non statique Une référence à une méthode non statique d'une classe
objet d'instance :: nom de méthode non statique Une référence de méthode non statique à l'objet d'instance spécifié de la classe
nom de classe :: nouveau référence du constructeur de classe

4.2 Exemples

Tout d'abord, nous écrivons une classe d'outil de collecte et fournissons une méthode :

    public class CollectionUtil{
    
    
        /**
         * 利用function将list集合中的每一个元素转换后形成新的集合返回
         * @param list 要转换的源集合
         * @param function 转换元素的方式
         * @param <T> 源集合的元素类型
         * @param <R> 转换后的元素类型
         * @return
         */
        public static <T,R> List<R> convert(List<T> list, Function<T,R> function){
    
    
            List<R> result = new ArrayList<>();
            list.forEach(t -> result.add(function.apply(t)));
            return result;
        }
    }

Vous pouvez voir que cette méthode reçoit deux paramètres :

  • List<T> list: la collection à transformer
  • Function<T,R>: Interface de fonction, recevant le type T et retournant le type R. Utilisez cette interface de fonction pour convertir l'élément T de la liste en type R

Examinons ensuite des cas particuliers :

4.2.1 Références de méthodes statiques des classes

List<Integer> list = Arrays.asList(1000, 2000, 3000);

Nous devons convertir les éléments de cette collection en hexadécimal, et nous devons appeler Integer.toHexString()la méthode :

public static String toHexString(int i) {
    
    
    return toUnsignedString0(i, 4);
}

Cette méthode accepte un type i et renvoie un Stringtype qui peut être utilisé pour construire une Functioninterface fonctionnelle :

Suivons d'abord la méthode d'écriture originale de Lambda, l'expression Lambda entrante sera compilée dans une interface, et les éléments de la collection originale sont convertis Functiondans l'interface :Integer.toHexString(i)

// 通过Lambda表达式实现
List<String> hexList = CollectionUtil.convert(list, i -> Integer.toHexString(i));
System.out.println(hexList);// [3e8, 7d0, bb8]

Dans le bloc de code d'expression Lambda ci-dessus, il n'y a qu'une Integer.toHexString()référence à la méthode, et il n'y a pas d'autre code, nous pouvons donc directement passer la méthode en tant que paramètre, et le compilateur la gérera pour nous. :

// 类的静态方法引用
List<String> hexList = CollectionUtil.convert(list, Integer::toHexString;
System.out.println(hexList);// [3e8, 7d0, bb8]

4.2.2 Références de méthodes non statiques des classes

Ensuite, nous capitalisons les éléments de Stringla collection que nous venons de générer hexList, en utilisant la méthode toUpperCase() de la classe String :

public String toUpperCase() {
    
    
    return toUpperCase(Locale.getDefault());
}

Cette fois, il s'agit d'une méthode non statique, qui ne peut pas être appelée par un nom de classe, mais un objet d'instance, il y a donc quelques différences par rapport à l'implémentation précédente. Nous recevons chaque chaîne dans la collection s. Mais contrairement à ce qui précède, spas toUpperCase()le paramètre, mais l'appelant :

// 通过Lambda表达式,接收String数据,调用toUpperCase()
List<String> upperList = CollectionUtil.convert(hexList, s -> s.toUpperCase());
System.out.println(upperList);// [3E8, 7D0, BB8]

Étant donné que le corps du code n'a que toUpperCase()le bon appel, vous pouvez passer la méthode en tant que référence de paramètre, et vous pouvez toujours abréger :

// 类的成员方法
List<String> upperList = CollectionUtil.convert(hexList, String::toUpperCase);
System.out.println(upperList);// [3E8, 7D0, BB8]

4.2.3 Références de méthodes non statiques spécifiant des instances

L'exigence suivante est la suivante : Nous définissons d'abord un nombre Integer num = 2000, puis utilisons ce nombre pour comparer avec chaque nombre de la collection, et mettons le résultat de la comparaison dans une nouvelle collection. Pour comparer des objets, on peut utiliser Integerla compareTométhode :

public int compareTo(Integer anotherInteger) {
    
    
    return compare(this.value, anotherInteger.value);
}

Utilisez d'abord Lambda pour implémenter,

List<Integer> list = Arrays.asList(1000, 2000, 3000);

// 某个对象的成员方法
Integer num = 2000;
List<Integer> compareList = CollectionUtil.convert(list, i -> num.compareTo(i));
System.out.println(compareList);// [1, 0, -1]

num.compareTo(i)Semblable aux précédents, il n'y a toujours que des appels corrects dans le bloc de code Lambda ici , il peut donc être abrégé. Cependant, il convient de noter que l'appelant de la méthode n'est pas un élément de la collection, mais une variable locale externe num, il ne peut donc pas être utilisé Integer::compareTo, car l'appelant de la méthode ne peut pas être déterminé de cette manière. Pour spécifier l'appelant, vous devez utiliser 对象::方法名:

// 某个对象的成员方法
Integer num = 2000;
List<Integer> compareList = CollectionUtil.convert(list, num::compareTo);
System.out.println(compareList);// [1, 0, -1]

4.2.4 Références constructeurs

La dernière scène : utilisez les nombres de la collection comme valeur de milliseconde, construisez Datel'objet et placez-le dans la collection, ici nous devons utiliser le constructeur de Date :

/**
  * @param   date   the milliseconds since January 1, 1970, 00:00:00 GMT.
  * @see     java.lang.System#currentTimeMillis()
  */
public Date(long date) {
    
    
    fastTime = date;
}

Nous pouvons recevoir chaque élément de la collection, puis passer l'élément en tant que Dateparamètre du constructeur :

// 将数值类型集合,转为Date类型
List<Date> dateList = CollectionUtil.convert(list, i -> new Date(i));
// 这里遍历元素后需要打印,因此直接把println作为方法引用传递了
dateList.forEach(System.out::println);

Dans l'implémentation de l'expression Lambda ci-dessus, le corps du code n'a qu'une new Date()seule ligne de code, il peut donc également être abrégé par référence de méthode. Mais le problème est que le constructeur n'a pas de nom, nous ne pouvons utiliser que newdes mots clés à la place :

// 构造方法
List<Date> dateList = CollectionUtil.convert(list, Date::new);
dateList.forEach(System.out::println);

Notez deux points :

  • System.out::println dans le code ci-dessus est en fait une référence à la méthode non statique println de l'objet spécifié System.out
  • S'il y a plusieurs constructeurs, il peut ne pas être possible de faire la distinction et de faire échouer le transfert

5. Méthodes par défaut et méthodes statiques des interfaces

Java 8 élargit la signification d'une interface avec deux nouveaux concepts : les méthodes par défaut et les méthodes statiques.

5.1 Méthode par défaut

La méthode par défaut permet aux développeurs d'ajouter de nouvelles méthodes aux interfaces existantes sans rompre la compatibilité binaire, c'est-à-dire qu'elle n'oblige pas les classes qui implémentent l'interface à implémenter également la méthode nouvellement ajoutée.

La différence entre les méthodes par défaut et les méthodes abstraites est que les méthodes abstraites nécessitent une implémentation, contrairement aux méthodes par défaut. La méthode par défaut fournie par l'interface sera héritée ou remplacée par la classe d'implémentation de l'interface. L'exemple de code est le suivant :

private interface Defaulable {
    
    
    // Interfaces now allow default methods, the implementer may or 
    // may not implement (override) them.
    default String notRequired() {
    
     
        return "Default implementation"; 
    }        
}

private static class DefaultableImpl implements Defaulable {
    
    
}

private static class OverridableImpl implements Defaulable {
    
    
    @Override
    public String notRequired() {
    
    
        return "Overridden implementation";
    }
}

L'interface Defaulable définit une méthode par défaut notRequired() en utilisant le mot clé default. La classe DefaultableImpl implémente cette interface et hérite par défaut de la méthode par défaut dans cette interface ; la classe OverridableImpl implémente également cette interface, mais remplace la méthode par défaut de l'interface et fournit une implémentation différente.

5.2 Méthodes statiques

Une autre fonctionnalité intéressante apportée par Java 8 est que les méthodes statiques peuvent être définies dans l'interface, et nous pouvons appeler directement ces méthodes statiques avec l'interface. L'exemple de code est le suivant :

private interface DefaulableFactory {
    
    
    // Interfaces now allow static methods
    static Defaulable create( Supplier< Defaulable > supplier ) {
    
    
        return supplier.get();
    }
}

L'extrait de code suivant combine des scénarios d'utilisation de méthode par défaut et de méthode statique :

public static void main( String[] args ) {
    
    
    // 调用接口的静态方法,并且传递DefaultableImpl的构造函数引用来构建对象
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );
	// 调用接口的静态方法,并且传递OverridableImpl的构造函数引用来构建对象
    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}

La sortie de ce code est la suivante :

Default implementation
Overridden implementation

Étant donné que l'implémentation de la méthode par défaut sur la JVM fournit un support au niveau du bytecode, elle est très efficace. Les méthodes par défaut permettent d'améliorer les interfaces sans casser les hiérarchies d'héritage existantes. L'application de cette fonctionnalité dans la bibliothèque officielle est : l'ajout de nouvelles méthodes à l' java.util.Collectioninterface, telles que stream(), parallelStream(), forEach()etc.removeIf()

Bien que la méthode par défaut présente de nombreux avantages, elle doit être utilisée avec prudence dans le développement réel : dans un système d'héritage complexe, la méthode par défaut peut provoquer des ambiguïtés et des erreurs de compilation. Si vous souhaitez en savoir plus, vous pouvez vous référer à la documentation officielle.

6. Facultatif

Le bogue le plus courant dans les applications Java est les exceptions nulles.

OptionalJuste un conteneur qui peut stocker des valeurs de type T ou null. Il fournit quelques interfaces utiles pour éviter nullles vérifications explicites, vous pouvez vous référer à la documentation officielle de Java 8 pour plus de détails.

Regardons un exemple d'utilisation de Optional : une valeur qui peut être vide ou une valeur d'un certain type :

Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );        
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); 
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );

Si Optionall'instance contient une valeur non nulle, isPresent()la méthode renvoie true, sinon elle renvoie false; si Optionall'instance contient null, orElseGet()la méthode peut accepter une valeur par défaut générée par une expression lambda ; map()la méthode peut convertir la valeur de l' Optionalinstance existante en une nouvelle valeur ; orElse()la méthode est identique à orElseGet()La méthode est similaire, mais renvoie la valeur par défaut transmise lorsque la valeur null est maintenue, au lieu d'être générée par Lambda.

La sortie du code ci-dessus est la suivante :

Full Name is set? false
Full Name: [none]
Hey Stranger!

Prenons un autre exemple simple :

Optional< String > firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );        
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) ); 
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();

La sortie de cet exemple est :

First Name is set? true
First Name: Tom
Hey Tom!

Pour plus de détails, veuillez vous référer à la documentation officielle.

7. Flux

La nouvelle API Stream (java.util.stream) apporte la programmation fonctionnelle de l'environnement de production à la bibliothèque Java. Il s'agit de loin de la plus grande amélioration de la bibliothèque Java afin que les développeurs puissent écrire un code plus efficace, concis et compact.

L'API Steam simplifie grandement les opérations de collecte (nous verrons plus que de simples collections plus tard), regardez d'abord cette classe appelée Task :

public class Streams  {
    
    
    private enum Status {
    
    
        OPEN, CLOSED
    };

    private static final class Task {
    
    
        private final Status status;
        private final Integer points;

        Task( final Status status, final Integer points ) {
    
    
            this.status = status;
            this.points = points;
        }

        public Integer getPoints() {
    
    
            return points;
        }

        public Status getStatus() {
    
    
            return status;
        }

        @Override
        public String toString() {
    
    
            return String.format( "[%s, %d]", status, points );
        }
    }
}

La classe Task a une propriété points, et il y a deux états : OUVERT ou FERMÉ. Supposons maintenant qu'il existe une collection de tâches :

final Collection< Task > tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 ) 
);

Examinez d'abord une question : Combien d'états OUVERTS y a-t-il dans cet ensemble de tâches ? Calculez leur somme d'attributs de points. Avant Java 8, pour résoudre ce problème, vous devez utiliser la boucle foreach pour parcourir la collection de tâches ; mais dans Java 8, vous pouvez utiliser steams pour le résoudre : il inclut une liste d'une série d'éléments et prend en charge le traitement séquentiel et parallèle .

// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );

La sortie de console de l'exécution de cette méthode est :

Total points: 18

De nombreux points de connaissance méritent d'être mentionnés ici. Premièrement, tasksla collection est convertie en une représentation ; deuxièmement steam, les opérations sur le filtre les éliminent toutes ; troisièmement, les opérations convertissent les flux en collections en fonction des méthodes de chaque instance de la collection ; enfin, les sommes sont calculées par des méthodes, ce qui donne le résultat final.steamfilterCLOSEDtaskmapToInttaskstaskTask::getPointstaskIntegersum

Avant d'apprendre l'exemple suivant, vous devez vous rappeler quelques points de connaissance des vapeurs (cliquez ici pour plus de détails). Les opérations sur Steam peuvent être divisées en opérations intermédiaires et en opérations tardives.

L'opération intermédiaire renverra une nouvelle vapeur - effectuer une opération intermédiaire (telle qu'un filtre) n'effectue pas l'opération de filtrage proprement dite, mais crée une nouvelle vapeur et place les éléments éligibles de la vapeur d'origine dans la vapeur nouvellement créée.

Les opérations tardives (telles que forEach ou sum) traverseront la vapeur et obtiendront le résultat ou les résultats accessoires ; après l'exécution de l'opération tardive, la ligne de traitement à la vapeur a été traitée et ne peut pas être utilisée. Dans presque tous les cas, les opérations tardives traversent la vapeur immédiatement.

Une autre valeur de steam est le support créatif pour le traitement parallèle (traitement parallèle). Pour l'ensemble de tâches ci-dessus, nous pouvons utiliser le code suivant pour calculer la somme des points de toutes les tâches :

// Calculate total points of all tasks
final double totalPoints = tasks
   .stream()
   .parallel()
   .map( task -> task.getPoints() ) // or map( Task::getPoints ) 
   .reduce( 0, Integer::sum );

System.out.println( "Total points (all tasks): " + totalPoints );

Ici, nous utilisons la méthode parallèle pour traiter toutes les tâches en parallèle et utilisons la méthode de réduction pour calculer le résultat final. La sortie de la console est la suivante :

Total points(all tasks): 26.0

Pour une collection, il est souvent nécessaire d'y regrouper des éléments selon certaines conditions. L'utilisation de l'API fournie par steam permet de réaliser rapidement de telles tâches, le code est le suivant :

// Group tasks by their status
final Map< Status, List< Task > > map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );

La sortie de la console est la suivante :

{
    
    CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

Le dernier exemple de question sur l'ensemble de tâches est : comment calculer la proportion des points de chaque tâche dans l'ensemble dans l'ensemble, le code de traitement spécifique est le suivant :

// Calculate the weight of each tasks (as percent of total points) 
final Collection< String > result = tasks
    .stream()                                        // Stream< String >
    .mapToInt( Task::getPoints )                     // IntStream
    .asLongStream()                                  // LongStream
    .mapToDouble( points -> points / totalPoints )   // DoubleStream
    .boxed()                                         // Stream< Double >
    .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
    .mapToObj( percentage -> percentage + "%" )      // Stream< String> 
    .collect( Collectors.toList() );                 // List< String > 

System.out.println( result );

La sortie de la console est la suivante :

[19%, 50%, 30%]

Enfin, comme mentionné précédemment, l'API Steam ne peut pas seulement agir sur les collections Java, les opérations IO traditionnelles (lecture de données ligne par ligne depuis un fichier ou un réseau) peuvent bénéficier d'un traitement steam, voici un petit exemple :

final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
    
    
    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}

Les méthodes de Stream onClose()renvoient un Stream équivalent avec le handle supplémentaire qui close()sera exécuté lorsque la méthode de Stream sera appelée. L'API Stream, les expressions Lambda et les références de méthode prises en charge par les méthodes d'interface par défaut et les méthodes statiques sont la réponse de Java 8 au paradigme moderne du développement logiciel.

8. Réseaux parallèles

La version Java 8 a ajouté de nombreuses nouvelles méthodes pour prendre en charge le traitement de tableau parallèle. Plus important encore parallelSort(), il peut considérablement accélérer le tri des tableaux sur les machines multicœurs. Les exemples suivants illustrent les méthodes de la famille parallelXxx :

package com.javacodegeeks.java8.parallel.arrays;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

public class ParallelArrays {
    
    
    public static void main( String[] args ) {
    
    
        long[] arrayOfLong = new long [ 20000 ];        

        Arrays.parallelSetAll( arrayOfLong, 
            index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
            i -> System.out.print( i + " " ) );
        System.out.println();

        Arrays.parallelSort( arrayOfLong );        
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
            i -> System.out.print( i + " " ) );
        System.out.println();
    }
}

Les codes ci-dessus utilisent la méthode parallelSetAll() pour générer 20 000 nombres aléatoires, puis utilisent la méthode parallelSort() pour les trier. Ce programme génère les 10 premiers éléments des tableaux non ordonnés et triés. La sortie de l'exemple de code ci-dessus est :

Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 
Sorted: 39 220 263 268 325 607 655 678 723 793

Je suppose que tu aimes

Origine blog.csdn.net/qq_31686241/article/details/128105924
conseillé
Classement