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) }
}