In Java 8 gibt es eine neue Methode, String.chars()
die einen Stream von int
s ( IntStream
) zurückgibt , der die Zeichencodes darstellt. Ich denke, viele Leute würden char
hier stattdessen einen Strom von s erwarten . Was war die Motivation, die API so zu gestalten?
194
CharStream
es nicht existiert, was wäre das Problem, um es hinzuzufügen?Antworten:
Wie bereits erwähnt, bestand die Entwurfsentscheidung dahinter darin, die Explosion von Methoden und Klassen zu verhindern.
Persönlich denke ich jedoch, dass dies eine sehr schlechte Entscheidung war, und es sollte, da sie keine
CharStream
vernünftigen, anderen Methoden treffen wollenchars()
, als ich denken würde:Stream<Character> chars()
, das gibt einen Strom von Box-Zeichen, die einige leichte Leistungseinbußen haben.IntStream unboxedChars()
, die für den Leistungscode verwendet werden würde.Jedoch , anstatt sich auf , warum es auf diese Weise zur Zeit geschehen ist, ich glaube , diese Antwort auf zeigt eine Art und Weise konzentrieren sollte es mit der API zu tun , dass wir mit Java 8 bekommen haben.
In Java 7 hätte ich es so gemacht:
Und ich denke, eine vernünftige Methode, dies in Java 8 zu tun, ist die folgende:
Hier erhalte ich eine
IntStream
und ordne sie über das Lambda einem Objekt zu. Dadurchi -> (char)i
wird sie automatisch in eine Box verpackt.Stream<Character>
Dann können wir tun, was wir wollen, und weiterhin Methodenreferenzen als Plus verwenden.Sei dir aber bewusst, dass du jedoch
mapToObj
, dass Siemap
nichts tun müssen , wenn Sie vergessen und verwenden , dann wird sich nichts beschweren, aber Sie werden trotzdem mit einem endenIntStream
, und Sie werden sich möglicherweise nicht mehr fragen, warum die ganzzahligen Werte anstelle der Zeichenfolgen gedruckt werden, die die Zeichen darstellen.Andere hässliche Alternativen für Java 8:
Wenn Sie in einer
IntStream
Datei bleiben und diese letztendlich drucken möchten, können Sie keine Methodenreferenzen mehr zum Drucken verwenden:Darüber hinaus funktioniert die Verwendung von Methodenreferenzen auf Ihre eigene Methode nicht mehr! Folgendes berücksichtigen:
und dann
Dies führt zu einem Kompilierungsfehler, da möglicherweise eine verlustbehaftete Konvertierung vorliegt.
Fazit:
Die API wurde auf diese Weise entwickelt, weil sie nicht hinzugefügt werden
CharStream
soll. Ich persönlich denke, dass die Methode a zurückgeben sollteStream<Character>
, und die Problemumgehung besteht derzeit darin,mapToObj(i -> (char)i)
auf aIntStream
zu arbeiten, um ordnungsgemäß mit ihnen arbeiten zu können.quelle
codePoints()
anstelle von zu verwenden,chars()
und Sie werden viele Bibliotheksfunktionen finden, die bereits einenint
for-Code-Punkt zusätzlich akzeptierenchar
, z. B. alle Methoden vonjava.lang.Character
sowieStringBuilder.appendCodePoint
usw. Diese Unterstützung besteht seitdemjdk1.5
.String
oder dargestellt werdenchar[]
. Ich wette, dass die meistenchar
Verarbeitungscodes Ersatzpaare falsch handhaben.void print(int ch) { System.out.println((char)ch); }
und dann können Sie Methodenreferenzen verwenden.Stream<Character>
abgelehnt wurde.Die Antwort von Skiwi deckte bereits viele wichtige Punkte ab. Ich werde etwas mehr Hintergrundinformationen ausfüllen.
Das Design einer API besteht aus einer Reihe von Kompromissen. In Java besteht eines der schwierigen Probleme darin, sich mit Entwurfsentscheidungen zu befassen, die vor langer Zeit getroffen wurden.
Primitive sind seit 1.0 in Java. Sie machen Java zu einer "unreinen" objektorientierten Sprache, da die Grundelemente keine Objekte sind. Das Hinzufügen von Grundelementen war meines Erachtens eine pragmatische Entscheidung, die Leistung auf Kosten der objektorientierten Reinheit zu verbessern.
Dies ist ein Kompromiss, mit dem wir heute, fast 20 Jahre später, noch leben. Die in Java 5 hinzugefügte Autoboxing-Funktion beseitigte größtenteils die Notwendigkeit, den Quellcode mit Box- und Unboxing-Methodenaufrufen zu überladen, aber der Overhead ist immer noch vorhanden. In vielen Fällen fällt es nicht auf. Wenn Sie jedoch das Boxen oder Entpacken innerhalb einer inneren Schleife durchführen, werden Sie feststellen, dass dies einen erheblichen Aufwand für die CPU- und Speicherbereinigung bedeuten kann.
Beim Entwerfen der Streams-API war klar, dass wir Grundelemente unterstützen mussten. Der Overhead beim Boxen / Unboxen würde jeden Leistungsvorteil durch Parallelität zunichte machen. Wir wollten jedoch nicht alle Grundelemente unterstützen, da dies der API eine Menge Unordnung hinzugefügt hätte. (Können Sie wirklich eine Verwendung für a sehen
ShortStream
?) "Alle" oder "Keine" sind bequeme Orte für ein Design, aber keines war akzeptabel. Also mussten wir einen vernünftigen Wert von "einigen" finden. Am Ende haben wir mit primitiven Spezialisierungen fürint
,long
unddouble
. (Persönlich hätte ich ausgelassen,int
aber das bin nur ich.)Denn
CharSequence.chars()
wir haben überlegt zurückzukehrenStream<Character>
(ein früher Prototyp könnte dies implementiert haben), aber es wurde wegen des Boxaufwands abgelehnt. Wenn man bedenkt, dass ein String hatchar
Werte als Grundelemente enthält, scheint es ein Fehler zu sein, das Boxen bedingungslos aufzuerlegen, wenn der Aufrufer den Wert wahrscheinlich nur ein wenig verarbeitet und ihn sofort wieder in eine Zeichenfolge entpackt.Wir haben auch eine
CharStream
primitive Spezialisierung in Betracht gezogen , aber ihre Verwendung scheint im Vergleich zu der Menge an Masse, die sie der API hinzufügen würde, ziemlich eng zu sein. Es schien sich nicht zu lohnen, es hinzuzufügen.Die Strafe, die dies für Anrufer bedeutet, ist, dass sie wissen müssen, dass die
IntStream
enthaltenenchar
Werte als dargestellt sindints
und dass das Casting an der richtigen Stelle durchgeführt werden muss. Dies ist verwirrend , weil es doppelt API - Aufrufe wie überlastet sindPrintStream.print(char)
undPrintStream.print(int)
dadurch unterscheiden , dass deutlich in ihrem Verhalten. Ein zusätzlicher Punkt der Verwirrung entsteht möglicherweise, weil dercodePoints()
Aufruf auch ein zurückgibtIntStream
, die darin enthaltenen Werte jedoch sehr unterschiedlich sind.Dies läuft darauf hinaus, pragmatisch zwischen mehreren Alternativen zu wählen:
Wir konnten keine primitiven Spezialisierungen bereitstellen, was zu einer einfachen, eleganten und konsistenten API führte, die jedoch eine hohe Leistung und einen hohen GC-Overhead erfordert.
Wir könnten einen vollständigen Satz primitiver Spezialisierungen bereitstellen, was die API überladen und den JDK-Entwicklern einen Wartungsaufwand auferlegen würde. oder
Wir könnten eine Teilmenge primitiver Spezialisierungen bereitstellen und eine mittelgroße, leistungsstarke API bereitstellen, die Anrufern in einem relativ engen Bereich von Anwendungsfällen eine relativ geringe Belastung auferlegt (Zeichenverarbeitung).
Wir haben den letzten gewählt.
quelle
chars()
, eine, die einStream<Character>
(mit geringem Leistungsverlust) zurückgibt, und eine andereIntStream
, wurde dies ebenfalls in Betracht gezogen? Es ist sehr wahrscheinlich, dass die Leute esStream<Character>
ohnehin einem zuordnen, wenn sie der Meinung sind, dass sich die Überzeugung über die Leistungsstrafe lohnt.chars()
Methode gibt, die die Zeichenwerte in einem zurückgibtIntStream
, wird nicht viel hinzugefügt, um einen weiteren API-Aufruf zu haben, der dieselben Werte erhält, jedoch in Boxform. Der Anrufer kann die Werte ohne großen Aufwand boxen. Sicher, es wäre bequemer, dies in diesem (wahrscheinlich seltenen) Fall nicht tun zu müssen, sondern auf Kosten des Hinzufügens von Unordnung zur API.chars()
RückgabeIntStream
kein großes Problem darstellt, insbesondere angesichts der Tatsache, dass diese Methode überhaupt selten angewendet wird. Es wäre jedoch gut, eine eingebaute Möglichkeit zu haben, um wiederIntStream
auf die zu konvertierenString
. Es kann getan werden.reduce(StringBuilder::new, (sb, c) -> sb.append((char)c), StringBuilder::append).toString()
, aber es ist wirklich lang.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString()
. Ich denke, es ist nicht wirklich kürzer, aber die Verwendung von Codepunkten vermeidet die(char)
Umwandlung und ermöglicht die Verwendung von Methodenreferenzen. Außerdem werden Leihmütter richtig behandelt.IntStream
keinecollect()
Methode, die aCollector
. Sie haben nur eine Drei-Argumente-collect()
Methode, wie in früheren Kommentaren erwähnt.