为什么String.chars()在Java8中是一个整数流?
在Java8中,有一个新方法返回表示字符代码的为什么String.chars()在Java8中是一个整数流?,java,string,java-8,Java,String,Java 8,在Java8中,有一个新方法返回表示字符代码的ints(IntStream)流。我猜很多人会希望这里有一个chars流。以这种方式设计API的动机是什么?正如其他人已经提到的,这背后的设计决策是为了防止方法和类的爆炸 不过,我个人认为这是一个非常糟糕的决定,考虑到他们不想使用CharStream,这是合理的,应该使用不同的方法而不是chars(),我会想到: Stream chars(),它提供了一个box字符流,这将有一些轻微的性能损失 IntStream unbexedchars(),将用
int
s(IntStream
)流。我猜很多人会希望这里有一个char
s流。以这种方式设计API的动机是什么?正如其他人已经提到的,这背后的设计决策是为了防止方法和类的爆炸
不过,我个人认为这是一个非常糟糕的决定,考虑到他们不想使用CharStream
,这是合理的,应该使用不同的方法而不是chars()
,我会想到:
,它提供了一个box字符流,这将有一些轻微的性能损失Stream chars()
,将用于性能代码IntStream unbexedchars()
for (int i = 0; i < hello.length(); i++) {
System.out.println(hello.charAt(i));
}
在这里,我获取一个IntStream
,并通过lambdaI->(char)I
将其映射到一个对象,这将自动将其装箱到一个流中,然后我们可以做我们想做的事情,并且仍然使用方法引用作为加号
请注意尽管您必须执行mapToObj
,但如果您忘记并使用map
,则不会有任何抱怨,但您仍然会得到一个IntStream
,您可能会想知道为什么它会打印整数值而不是代表字符的字符串
其他丑陋的Java 8替代方案:
通过留在IntStream
中并希望最终打印它们,您无法再使用方法引用进行打印:
hello.chars()
.forEach(i -> System.out.println((char)i));
此外,对自己的方法使用方法引用不再有效!考虑以下事项:
hello.chars()
.mapToObj(i -> (char)i)
.forEach(System.out::println);
private void print(char c) {
System.out.println(c);
}
然后
hello.chars()
.forEach(this::print);
这将导致编译错误,因为可能存在有损转换
结论:
API是这样设计的,因为不想添加CharStream
,我个人认为该方法应该返回Stream
,目前的解决方法是使用mapToObj(I->(char)I)
在IntStream上
,以便能够正确使用它们。本手册已经涵盖了许多要点。我会补充一些背景知识
任何API的设计都是一系列权衡。在Java中,一个困难的问题是处理很久以前做出的设计决策
从1.0开始,原语就一直使用Java。它们使Java成为一种“不纯”的面向对象语言,因为原语不是对象。我认为,添加原语是一个以牺牲面向对象的纯洁性为代价来提高性能的务实决定
近20年后的今天,这是一个我们仍然生活在其中的权衡。Java5中添加的自动装箱特性基本上消除了用装箱和拆箱方法调用来混乱源代码的需要,但是开销仍然存在。在许多情况下,这并不明显。但是,如果要在内部循环中执行装箱或取消装箱,您会看到它会带来显著的CPU和垃圾收集开销
在设计Streams API时,很明显我们必须支持原语。装箱/拆箱开销会破坏并行性带来的任何性能好处。不过,我们不想支持所有的原语,因为这会给API增加大量的混乱。(你真的能看到短流的用途吗?)“全部”或“无”对于设计来说都是舒适的地方,但两者都不可接受。因此,我们必须找到“some”的合理值。我们最终得到了int
、long
和double
的基本专门化。(就我个人而言,我可能会忽略int
,但那只是我自己。)
对于CharSequence.chars()
,我们考虑返回Stream
(早期原型可能已经实现了这一点),但由于装箱开销,它被拒绝了。考虑到字符串将char
值作为原语,当调用方可能只对值进行一点处理并将其重新装箱到字符串中时,无条件地强制装箱似乎是错误的
我们还考虑了CharStream
原语专门化,但与它将添加到API中的批量相比,它的使用范围似乎非常狭窄。似乎不值得添加它
这对调用者造成的惩罚是,他们必须知道IntStream
包含char
表示为ints
的值,并且必须在适当的位置进行转换。这让人倍感困惑,因为存在重载API调用,如PrintStream.print(char)
和PrintStream.print(int)
,它们的行为明显不同。另一个混淆点可能会出现,因为codePoints()
调用还返回一个IntStream
,但它包含的值完全不同
因此,这归结为从几个备选方案中进行务实选择:
我们不能提供原始的专门化,从而产生一个简单、优雅、一致的API,但这会带来高性能和GC开销
我们可以提供一套完整的原始专门化,代价是将API弄得一团糟,并给JDK开发人员带来维护负担;或
我们可以提供原语专门化的子集,提供一个中等大小、高性能的API,在相当狭窄的用例范围(字符处理)中对调用方施加相对较小的负担
我们选择了最后一个。@RohitJain