Dans Java 8, il existe une nouvelle méthode String.chars()
qui renvoie un flux de int
s ( IntStream
) représentant les codes de caractères. Je suppose que beaucoup de gens s'attendraient à un flux de char
s ici à la place. Quelle était la motivation pour concevoir l'API de cette façon?
203
CharStream
n'existe pas quel serait le problème de l'ajouter?Réponses:
Comme d'autres l'ont déjà mentionné, la décision de conception derrière cela était d'empêcher 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 prendre
CharStream
, ce qui est raisonnable, des méthodes différentes au lieu dechars()
, je penserais à:Stream<Character> chars()
, cela donne un flux de caractères de boîtes, ce qui aura une légère pénalité de performance.IntStream unboxedChars()
, qui serait utilisé pour le code de performance.Cependant , au lieu de se concentrer sur les raisons pour lesquelles cela est fait de cette façon actuellement, je pense que cette réponse devrait se concentrer sur montrer un moyen de le faire avec l'API que nous avons obtenue avec Java 8.
Dans 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 dans Java 8 est la suivante:
hello.chars() .mapToObj(i -> (char)i) .forEach(System.out::println);
Ici, j'obtiens un
IntStream
et le mappe à un objet via le lambdai -> (char)i
, cela le placera automatiquement dans unStream<Character>
, et nous pourrons alors faire ce que nous voulons, et continuer à utiliser des références de méthode comme un plus.Sachez cependant que vous devez le faire
mapToObj
, si vous oubliez et utilisezmap
, alors rien ne se plaindra, mais vous vous retrouverez quand même avec unIntStream
, et vous pourriez vous demander pourquoi il imprime les valeurs entières au lieu des chaînes représentant les caractères.Autres alternatives laides pour Java 8:
En restant dans un
IntStream
et en voulant les imprimer finalement, vous ne pouvez plus utiliser de références de méthode pour l'impression:hello.chars() .forEach(i -> System.out.println((char)i));
De plus, utiliser des 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); }
puis
hello.chars() .forEach(this::print);
Cela donnera une erreur de compilation, car il y a peut-être une conversion avec perte.
Conclusion:
L'API a été conçue de cette façon parce que
CharStream
je ne veux pas ajouter , je pense personnellement que la méthode devrait renvoyer unStream<Character>
, et la solution de contournement consiste actuellement à utilisermapToObj(i -> (char)i)
sur unIntStream
pour pouvoir fonctionner correctement avec eux.la source
codePoints()
place dechars()
et vous trouverez de nombreuses fonctions de bibliothèque acceptant déjà unint
point de code for en plus dechar
, par exemple toutes les méthodes dejava.lang.Character
ainsi queStringBuilder.appendCodePoint
, etc. Ce support existe depuisjdk1.5
.String
ouchar[]
. Je parie que la plupartchar
des codes de traitement gèrent mal les paires de substitution.void print(int ch) { System.out.println((char)ch); }
et vous pourrez ensuite utiliser des références de méthode.Stream<Character>
été rejeté.La réponse de skiwi couvrait déjà de nombreux points majeurs. Je vais compléter un peu plus le contexte.
La conception de toute API est une série de compromis. En Java, l'un des problèmes difficiles concerne les décisions de conception prises il y a longtemps.
Les primitives sont en Java depuis la version 1.0. Ils font de Java un langage orienté objet "impur", puisque les primitives ne sont pas des objets. L'ajout de primitives était, je crois, une décision pragmatique d'améliorer les performances au détriment de la pureté orientée objet.
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 dans Java 5 a éliminé la plupart du temps le besoin d'encombrer le code source avec des appels de méthodes de boxe et de déballage, mais la surcharge est toujours là. Dans de nombreux cas, ce n'est pas perceptible. Cependant, si vous deviez effectuer un boxing ou un déballage dans une boucle interne, vous verrez que cela peut imposer une surcharge importante du processeur et de la récupération de la mémoire.
Lors de la conception de l'API Streams, il était clair que nous devions prendre en charge les primitives. La surcharge de boxe / déballage tuerait tout avantage de performance du parallélisme. Cependant, nous ne voulions pas prendre en charge toutes les primitives, car cela aurait ajouté un encombrement énorme à l'API. (Pouvez-vous vraiment voir une utilité pour un
ShortStream
?) "Tous" ou "aucun" sont des endroits confortables pour un design, mais aucun n'était acceptable. Nous avons donc dû trouver une valeur raisonnable de «certains». Nous avons fini avec des spécialisations primitives pourint
,long
etdouble
. (Personnellement, j'aurais laissé de côtéint
mais ce n'est que moi.)Car
CharSequence.chars()
nous avons envisagé de revenirStream<Character>
(un premier prototype aurait pu l'implémenter) mais il a été rejeté en raison de la surcharge de boxe. Considérant qu'une chaîne a deschar
valeurs comme primitives, il semblerait être une erreur d'imposer la boxe inconditionnellement alors que l'appelant ferait probablement juste un peu de traitement sur la valeur et la déballerait directement dans une chaîne.Nous avons également envisagé une
CharStream
spécialisation primitive, mais son utilisation semble assez étroite par rapport à la quantité de volume qu'elle ajouterait à l'API. Cela ne semblait pas utile de l'ajouter.La pénalité que cela impose aux appelants est qu'ils doivent savoir que le
IntStream
contient deschar
valeurs représentées commeints
et que le casting doit être effectué au bon endroit. Ceci est doublement déroutant car il y a des appels d'API surchargés commePrintStream.print(char)
etPrintStream.print(int)
qui diffèrent considérablement dans leur comportement. Un point de confusion supplémentaire peut survenir car l'codePoints()
appel renvoie également unIntStream
mais les valeurs qu'il contient sont assez différentes.Donc, cela revient à choisir de manière pragmatique parmi plusieurs alternatives:
Nous ne pourrions fournir aucune spécialisation primitive, résultant en une API simple, élégante et cohérente, mais qui impose des performances et une surcharge GC élevées;
nous pourrions fournir un ensemble complet de spécialisations primitives, au prix d'encombrer l'API et d'imposer une charge de maintenance aux développeurs JDK; ou
nous pourrions fournir un sous-ensemble de spécialisations primitives, donnant une API de taille moyenne et performante qui impose une charge relativement faible aux appelants dans une gamme assez restreinte de cas d'utilisation (traitement de caractères).
Nous avons choisi le dernier.
la source
chars()
, l'une qui renvoie unStream<Character>
(avec une petite pénalité de performance) et l'autre étantIntStream
, cela a-t-il également été envisagé? Il est fort probable que les gens finiront par le mapper à un deStream<Character>
toute façon s'ils pensent que la commodité en vaut la peine par rapport à la pénalité de performance.chars()
méthode qui renvoie les valeurs char dans anIntStream
, cela n'ajoute pas grand-chose d'avoir un autre appel API qui obtient les mêmes valeurs mais sous forme encadrée. L'appelant peut encadrer les valeurs sans trop de problèmes. Bien sûr, il serait plus pratique de ne pas avoir à faire cela dans ce cas (probablement rare), mais au prix d'ajouter du fouillis à l'API.chars()
retourIntStream
n'est pas un gros problème d'autant plus que cette méthode est rarement utilisée du tout. Cependant , il serait bon d'avoir un moyen intégré pour reconvertirIntStream
auString
. Cela peut être fait avec.reduce(StringBuilder::new, (sb, c) -> sb.append((char)c), StringBuilder::append).toString()
, mais c'est vraiment long.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString()
. Je suppose que ce n'est pas vraiment plus court, mais l'utilisation de points de code évite les(char)
moulages et permet l'utilisation de références de méthode. De plus, il gère correctement les substituts.IntStream
n'ont pas decollect()
méthode qui prend unCollector
. Ils n'ont qu'unecollect()
méthode à trois arguments comme mentionné dans les commentaires précédents.