Pourquoi Ssortingng.chars () est-il un stream d’ints dans Java 8?

Dans Java 8, il existe une nouvelle méthode Ssortingng.chars() qui renvoie un stream de données int ( IntStream ) représentant les codes de caractères. Je suppose que beaucoup de gens s’attendent à un stream de char ici. Quelle était la motivation pour concevoir l’API de cette façon?

Comme d’autres l’ont déjà mentionné, la décision en la matière était de prévenir l’explosion des méthodes et des classes.

Pourtant, personnellement, je pense que c’était une très mauvaise décision, et il devrait y avoir, étant donné qu’ils ne veulent pas faire de CharStream , ce qui est raisonnable, des méthodes différentes à la place des chars() , je penserais à:

  • Stream chars() , qui donne un stream de caractères de cases, ce qui entraîne une légère perte de performance.
  • IntStream unboxedChars() , qui devrait être utilisé pour le code de performance.

Cependant , au lieu de se focaliser sur la raison pour laquelle il est fait de cette façon actuellement, je pense que cette réponse devrait se concentrer sur la façon de le faire avec l’API que nous avons obtenue avec Java 8.

En Java 7, je l’aurais fait comme ceci:

 for (int i = 0; i < hello.length(); i++) { System.out.println(hello.charAt(i)); } 

Et je pense qu'une méthode raisonnable pour le faire en Java 8 est la suivante:

 hello.chars() .mapToObj(i -> (char)i) .forEach(System.out::println); 

Ici, IntStream un IntStream et le mappe à un object via le lambda i -> (char)i , cela le placera automatiquement dans un Stream , et ensuite nous pourrons faire ce que nous voulons, tout en utilisant les références de méthode comme plus.

Sachez cependant que vous devez faire mapToObj , si vous oubliez et utilisez map , alors rien ne va se plaindre, mais vous allez quand même vous retrouver avec un IntStream , et vous risquez de vous demander pourquoi il imprime les valeurs entières au lieu des chaînes représentant le personnages.

Autres alternatives laides pour Java 8:

En restant dans un IntStream et en souhaitant les imprimer, vous ne pouvez plus utiliser les références de méthode pour imprimer:

 hello.chars() .forEach(i -> System.out.println((char)i)); 

De plus, l'utilisation de références de méthode à votre propre méthode ne fonctionne plus! Considérer ce qui suit:

 private void print(char c) { System.out.println(c); } 

et alors

 hello.chars() .forEach(this::print); 

Cela donnera une erreur de compilation, car il y a probablement une conversion avec perte.

Conclusion:

L'API a été conçue de cette façon car elle ne veut pas append CharStream . CharStream , je pense que la méthode devrait renvoyer un Stream , et la solution actuelle consiste à utiliser mapToObj(i -> (char)i) sur un IntStream capable de travailler correctement avec eux.

La réponse de skiwi couvrait déjà un grand nombre de points majeurs. Je vais remplir un peu plus de fond.

La conception de toute API est une série de compromis. En Java, l’une des questions les plus difficiles concerne les décisions de conception sockets depuis longtemps.

Les primitives sont en Java depuis la version 1.0. Ils font de Java un langage orienté object “impur”, puisque les primitives ne sont pas des objects. Je pense que l’ajout de primitives était une décision pragmatique visant à améliorer les performances au désortingment de la pureté orientée object.

C’est un compromis avec lequel nous vivons encore aujourd’hui, près de 20 ans plus tard. La fonctionnalité de mise en boîte automatique ajoutée à Java 5 élimine la nécessité d’encombrer le code source avec les appels de méthode de boxe et de déballage, mais la surcharge est toujours présente. Dans de nombreux cas, cela ne se remarque pas. Toutefois, si vous deviez effectuer un boxing ou un unboxing dans une boucle interne, vous verriez qu’il peut imposer une surcharge importante en termes de processeur et de récupération de place.

Lors de la conception de l’API Streams, il était clair que nous devions prendre en charge les primitives. La surcharge de boxing / unboxing tuerait tout avantage de performance du parallélisme. Nous ne voulions pas prendre en charge toutes les primitives, car cela aurait ajouté une énorme quantité d’encombrement à l’API. (Pouvez-vous vraiment voir une utilisation pour un ShortStream ?) “All” ou “none” sont des endroits confortables pour un design, mais ni l’un ni l’autre n’était acceptable. Nous avons donc dû trouver une valeur raisonnable de “certains”. Nous nous sums retrouvés avec des spécialisations primitives pour int , long et double . (Personnellement, j’aurais laissé int mais c’est juste moi.)

Pour CharSequence.chars() nous avons envisagé de renvoyer Stream (un premier prototype aurait peut-être implémenté cela), mais il a été rejeté à cause de la surcharge de boxe. Considérant qu’une chaîne a des valeurs de caractère en tant que primitives, il semblerait être une erreur d’imposer une boxe inconditionnellement lorsque l’appelant ferait probablement un peu de traitement sur la valeur et la déballerait dans une chaîne.

Nous avons également considéré une spécialisation primitive CharStream , mais son utilisation semble être assez étroite par rapport à la quantité de masse qu’elle appendait à l’API. Il ne semblait pas utile de l’append.

La pénalité que cela impose aux appelants est qu’ils doivent savoir que l’ IntStream contient des valeurs de caractère représentées sous la forme ints et que la diffusion doit être effectuée au bon endroit. Ceci est doublement déroutant car il y a des appels d’API surchargés comme PrintStream.print(char) et PrintStream.print(int) qui diffèrent nettement dans leur comportement. Un sharepoint confusion supplémentaire peut codePoints() car l’appel à codePoints() renvoie également un IntStream mais les valeurs qu’il contient sont très différentes.

Donc, cela revient à choisir de manière pragmatique parmi plusieurs alternatives:

  1. Nous ne pouvions pas fournir de spécialisations primitives, résultant en une API simple, élégante et cohérente, mais qui impose une haute performance et une surcharge GC;

  2. nous pourrions fournir un ensemble complet de spécialisations primitives, au prix de l’encombrement de l’API et de l’imposition d’une charge de maintenance aux développeurs JDK; ou

  3. nous pourrions fournir un sous-ensemble de spécialisations primitives, en fournissant une API de taille moyenne et à haute performance qui impose un fardeau relativement faible aux appelants dans une gamme assez étroite de cas d’utilisation (traitement de caractères).

Nous avons choisi le dernier.