В Java 8 есть новый метод, String.chars()
который возвращает поток int
s ( IntStream
), который представляет коды символов. Я предполагаю, что многие люди ожидали бы здесь поток char
s. Какова была мотивация для разработки API таким образом?
198
CharStream
не существует, что было бы проблемой, чтобы добавить его?Ответы:
Как уже упоминали другие, дизайнерское решение было предотвращать взрыв методов и классов.
Тем не менее, лично я думаю, что это было очень плохое решение, и поэтому, учитывая, что они не хотят принимать
CharStream
, что является разумным, другие методы вместоchars()
, я бы подумал:Stream<Character> chars()
, что дает поток ящиков символов, который будет иметь небольшое снижение производительности.IntStream unboxedChars()
, который будет использоваться для кода производительности.Однако вместо того, чтобы сосредоточиться на том, почему это делается в настоящее время, я думаю, что этот ответ должен сосредоточиться на том, чтобы показать способ сделать это с помощью API, который мы получили в Java 8.
В Java 7 я бы сделал это так:
И я думаю, что разумный способ сделать это в Java 8 заключается в следующем:
Здесь я получаю
IntStream
и сопоставляю его с объектом через лямбдуi -> (char)i
, это автоматически помещает его в aStream<Character>
, и тогда мы можем делать то, что хотим, и при этом использовать ссылки на методы как плюс.Однако имейте в виду, что вы должны это сделать
mapToObj
, если вы забудете и будете использоватьmap
, тогда ничто не будет жаловаться, но вы все равно останетесь с результатомIntStream
, и вас может не удивить, почему он печатает целочисленные значения вместо строк, представляющих символы.Другие уродливые альтернативы для Java 8:
Оставаясь в
IntStream
и желая в конечном итоге распечатать их, вы больше не можете использовать ссылки на методы для печати:Более того, использование ссылок на собственные методы больше не работает! Учтите следующее:
а потом
Это приведет к ошибке компиляции, так как возможно преобразование с потерями.
Вывод:
API был спроектирован таким образом, потому что не нужно добавлять
CharStream
, я лично считаю, что метод должен возвращать aStream<Character>
, и в настоящее время обходной путь заключается в том, чтобы использоватьmapToObj(i -> (char)i)
егоIntStream
для правильной работы с ними.источник
codePoints()
вместо,chars()
и вы найдете множество библиотечных функций, уже принимающихint
для кода точку дополнительноchar
, например, все методы,java.lang.Character
а такжеStringBuilder.appendCodePoint
, и т. Д. Эта поддержка существует с тех порjdk1.5
.String
илиchar[]
. Могу поспорить, что большая частьchar
кода обрабатывает суррогатные пары.void print(int ch) { System.out.println((char)ch); }
а затем вы можете использовать ссылки на методы.Stream<Character>
был отклонен.Ответ от skiwi покрыты многие из основных моментов уже. Я добавлю немного больше фона.
Дизайн любого API представляет собой серию компромиссов. В Java одна из сложных проблем связана с проектными решениями, которые были приняты давно.
Примитивы были в Java с 1.0. Они делают Java «нечистым» объектно-ориентированным языком, поскольку примитивы не являются объектами. Я полагаю, что добавление примитивов было прагматичным решением улучшить производительность за счет объектно-ориентированной чистоты.
Это компромисс, с которым мы все еще живем сегодня, почти 20 лет спустя. Функция автобоксирования, добавленная в Java 5, по большей части избавила от необходимости загромождать исходный код вызовами методов упаковки и распаковки, но накладные расходы все еще присутствуют. Во многих случаях это не заметно. Однако, если бы вы выполняли упаковку или распаковку внутри внутреннего цикла, вы бы увидели, что это может привести к значительным накладным расходам ЦП и сборке мусора.
При разработке Streams API было ясно, что мы должны поддерживать примитивы. Затраты на упаковку / распаковку убили бы любую выгоду производительности от параллелизма. Мы не хотели поддерживать все примитивы, поскольку это добавило бы огромное количество беспорядка в API. (Можете ли вы увидеть использование для
ShortStream
?) «Все» или «нет» - удобные места для дизайна, но ни один из них не был приемлемым. Таким образом, мы должны были найти разумное значение «некоторые». Мы закончили с примитивными специализациями дляint
,long
иdouble
. (Лично я бы не учел,int
но это только я.)Для
CharSequence.chars()
мы считали возвращениеStream<Character>
(ранний прототип мог бы реализовать это) , но он был отклонен из - за бокс накладных расходов. Учитывая, что String имеетchar
значения в качестве примитивов, было бы ошибкой навязывать бокс безоговорочно, когда вызывающая сторона, вероятно, просто немного обработает значение и распакует его обратно в строку.Мы также рассмотрели
CharStream
примитивную специализацию, но ее использование может показаться довольно узким по сравнению с объемом, который она добавит к API. Не стоило добавлять это.Наказывающий это налагает на абонентов то, что они должны знать, что
IntStream
содержитchar
значения, представленные как,ints
и что приведение должно быть выполнено в правильном месте. Это вдвойне сбивает с толку, потому что есть перегруженные вызовы API, подобныеPrintStream.print(char)
иPrintStream.print(int)
заметно отличающиеся по своему поведению. Возможно, возникаетcodePoints()
еще одна путаница, поскольку вызов также возвращает значение,IntStream
но содержащиеся в нем значения совершенно разные.Таким образом, это сводится к прагматическому выбору из нескольких альтернатив:
Мы не могли предоставить примитивную специализацию, что привело бы к простому, элегантному и согласованному API, но которое требовало бы высокой производительности и накладных расходов GC;
мы могли бы предоставить полный набор примитивных специализаций за счет загромождения API и наложения бремени обслуживания на разработчиков JDK; или
мы могли бы предоставить подмножество примитивных специализаций, предоставив высокопроизводительный API умеренного размера, который накладывает относительно небольшую нагрузку на вызывающих абонентов в довольно узком диапазоне вариантов использования (обработка символов).
Мы выбрали последний.
источник
chars()
: один, который возвращает aStream<Character>
(с небольшим снижением производительности), а другойIntStream
- это тоже было рассмотрено? Вполне вероятно, что люди всеStream<Character>
равно будут в конечном итоге сопоставлять его, если они считают, что удобство стоит того, чтобы снизить производительность.chars()
метод, который возвращает значения типа char в anIntStream
, он не добавляет большого значения, чтобы иметь другой вызов API, который получает те же значения, но в штучной форме. Вызывающий может поместить значения без особых проблем. Конечно, было бы удобнее не делать это в этом (вероятно, редком) случае, но за счет добавления беспорядка в API.chars()
возвращениеIntStream
не является большой проблемой, особенно учитывая тот факт, что этот метод используется редко вообще. Однако было бы хорошо иметь встроенный способ преобразования обратноIntStream
вString
. Это может быть сделано.reduce(StringBuilder::new, (sb, c) -> sb.append((char)c), StringBuilder::append).toString()
, но это действительно долго.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString()
. Я думаю, что это не совсем короче, но использование точек кода позволяет избежать(char)
приведения и позволяет использовать ссылки на методы. Плюс это обрабатывает суррогаты должным образом.IntStream
, не имеютcollect()
метода, который принимаетCollector
. У них есть толькоcollect()
метод с тремя аргументами, как упомянуто в предыдущих комментариях.