Java 将流转换为IntStream
我有一种感觉,我错过了一些东西。我发现自己在做以下事情Java 将流转换为IntStream,java,java-8,java-stream,Java,Java 8,Java Stream,我有一种感觉,我错过了一些东西。我发现自己在做以下事情 private static int getHighestValue(Map<Character, Integer> countMap) { return countMap.values().stream().mapToInt(Integer::intValue).max().getAsInt(); } private static int getHighestValue(Map countMap){ 返回countM
private static int getHighestValue(Map<Character, Integer> countMap) {
return countMap.values().stream().mapToInt(Integer::intValue).max().getAsInt();
}
private static int getHighestValue(Map countMap){
返回countMap.values().stream().mapToInt(Integer::intValue).max().getAsInt();
}
我的问题是通过mapToInt(Integer::intValue)
有没有更好的转换方法?所有这一切都是为了避免使用
Stream
中的max()
,这需要传递一个比较器
,但问题是如何将Stream
转换为IntStream
我意识到您正在试图避免比较器,但是您可以通过引用Integer.compareTo
来使用内置函数:
private static int getHighestValue(Map<Character, Integer> countMap) {
return countMap.values().stream().max(Integer::compareTo).get();
}
另一种转换方法是使用lambda:
mapToInt(i->i)
。
我们将详细讨论您应该使用lambda还是方法引用,但总结是您应该使用您认为更可读的任何引用。由于类型擦除,
流
实现不知道其元素的类型,也无法为您提供这两种类型的信息,简化的max
操作,也不是转换为IntStream
方法
在这两种情况下,它分别需要一个函数、比较器或ToIntFunction
,以使用流
元素的未知引用类型执行操作
要执行的操作的最简单形式是
return countMap.values().stream().max(Comparator.naturalOrder()).get();
鉴于自然顺序比较器是作为单例实现的。因此,如果对可比
元素进行了优化,那么它是唯一有机会被流
实现识别的比较器。如果没有这样的优化,由于其单例特性,它仍然是内存占用最少的变体
如果您坚持将流
转换为IntStream
,则无法提供ToIntFunction
,并且对于Number::intValue
类型的函数没有预定义的单例,因此使用Integer::intValue
已经是最佳选择。您可以改为编写i->i
,它比较短,但只是隐藏取消装箱操作。如果问题是“在从Stream
转换到IntStream
时,我是否可以避免传递转换器?”一个可能的答案可能是“Java中没有办法使这种转换类型安全,同时使其成为流
接口的一部分”
实际上,不使用转换器将流
转换为IntStream
的方法可能如下所示:
public interface Stream<T> {
// other methods
default IntStream mapToInt() {
Stream<Integer> intStream = (Stream<Integer>)this;
return intStream.mapToInt(Integer::intValue);
}
}
它将在以下情况下调用consumeIntStream()
时失败:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at java.util.stream.ReferencePipeline$4$1.accept(ReferencePipeline.java:210)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfInt.evaluateSequential(ForEachOps.java:189)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.IntPipeline.forEach(IntPipeline.java:404)
at streams.IntStreamTest.consumeIntStream(IntStreamTest.java:25)
at streams.IntStreamTest.main(IntStreamTest.java:10)
有了此stacktrace,您是否能够快速确定问题出在produceIntStream()
中,因为在错误类型的流上调用了mapToInt()
当然,可以编写类型安全的转换方法,因为它接受具体的流:
publicstaticintstreammaptoint(Stream-Stream){
返回stream.mapToInt(Integer::intValue);
}
//用法
IntStream IntStream=mapToInt(Arrays.asList(1,2,3).stream())
但它不是很方便,因为它破坏了流的流畅接口特性
顺便说一句:
Kotlin的扩展函数允许调用某些代码,因为它是类接口的一部分。因此,您可以将此类型安全方法作为流的方法调用:
// "adds" mapToInt() to Stream<java.lang.Integer>
fun Stream<java.lang.Integer>.mapToInt(): IntStream {
return this.mapToInt { it.toInt() }
}
@Test
fun test() {
Arrays.asList<java.lang.Integer>(java.lang.Integer(1), java.lang.Integer(2))
.stream()
.mapToInt()
.forEach { println(it) }
}
/“向流添加”mapToInt()
fun Stream.mapToInt():IntStream{
返回this.mapToInt{it.toInt()}
}
@试验
趣味测试(){
asList(java.lang.Integer(1),java.lang.Integer(2))
.stream()
.mapToInt()
.forEach{println(it)}
}
您是否考虑过使用max(Comparator.naturalOrder())
?为什么您认为这很愚蠢?您想返回一个int,所以.mapToInt()是有意义的…@fge,因为我认为我浪费了对autobox值的调用,这需要O(n)操作。我希望我可以直接将流作为IntStream来获取。不,因为集合或映射中的任何值都不能是原语;您在这里不是装箱,而是取消装箱。尽管如此,它并不像您想象的那样昂贵。我心里有一个真实的用例,但它太长了,无法在一条注释中解释。不,您没有进行装箱——值是你已经装箱了,你正在拆箱(你必须这样做才能对它们进行比较)。你做得对。你没有花费任何可以避免的计算;如果你在一个简单的旧循环中这么做,你仍然会做至少同样多的拆箱操作。与其完成get()
也许,orElse(0)
或orelsetrow(…)
可能更“安全”?@wassgren是的,这两种方法都很好,尤其是如果你有一个空地图的话。坦白说,我刚才用的是get()
因为我更关注比较器部分。这是一个很好的选择。我对此进行了投票,尽管它没有回答实际问题,因为我将使用它。但是,我只想知道将流转换为IntStream的最佳方式是什么,因为我发现我自己在其他情况下需要它。顺便说一句,可能是Stream().mapToInt(Integer::intValue)
是获得ReadyFunction.identity()的最佳方法
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at java.util.stream.ReferencePipeline$4$1.accept(ReferencePipeline.java:210)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfInt.evaluateSequential(ForEachOps.java:189)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.IntPipeline.forEach(IntPipeline.java:404)
at streams.IntStreamTest.consumeIntStream(IntStreamTest.java:25)
at streams.IntStreamTest.main(IntStreamTest.java:10)
public static IntStream mapToInt(Stream<Integer> stream) {
return stream.mapToInt(Integer::intValue);
}
// usage
IntStream intStream = mapToInt(Arrays.asList(1, 2, 3).stream())
// "adds" mapToInt() to Stream<java.lang.Integer>
fun Stream<java.lang.Integer>.mapToInt(): IntStream {
return this.mapToInt { it.toInt() }
}
@Test
fun test() {
Arrays.asList<java.lang.Integer>(java.lang.Integer(1), java.lang.Integer(2))
.stream()
.mapToInt()
.forEach { println(it) }
}