将Stream转换为IntStream

时间:2015-01-18 21:59:16

标签: java java-8 java-stream

我有一种感觉,我在这里遗漏了一些东西。我发现自己在做以下事情

private static int getHighestValue(Map<Character, Integer> countMap) {
    return countMap.values().stream().mapToInt(Integer::intValue).max().getAsInt();
}

我的问题是通过Stream

IntStreammapToInt(Integer::intValue)的愚蠢转换

有没有更好的转换方式?这一切都是为了避免使用max()中的Stream,这需要传递Comparator,但问题是关于Stream转换为IntStream

4 个答案:

答案 0 :(得分:10)

我意识到您正在尝试避免使用比较器,但您可以通过引用Integer.compareTo来使用内置功能:

private static int getHighestValue(Map<Character, Integer> countMap) {
    return countMap.values().stream().max(Integer::compareTo).get();
}

或@fge建议,使用::compare

private static int getHighestValue(Map<Character, Integer> countMap) {
    return countMap.values().stream().max(Integer::compare).get();
}

答案 1 :(得分:10)

由于类型擦除,Stream实现不了解其元素的类型,也无法为您提供简化的max操作,也无法转换为IntStream方法

在这两种情况下,它分别需要一个函数ComparatorToIntFunction来使用Stream元素的未知引用类型执行操作。

您要执行的操作的最简单形式是

return countMap.values().stream().max(Comparator.naturalOrder()).get();

考虑到自然顺序比较器实现为单例的事实。因此,如果有关于Stream元素的任何优化,它是唯一能够被Comparable实现识别的比较器。如果没有这样的优化,由于其单独特性,它仍然是具有最低内存占用的变体。

如果您坚持要将Stream转换为IntStream,则无法提供ToIntFunction并且Number::intValue种类没有预定义的单例功能,所以使用Integer::intValue已经是最好的选择。您可以改为编写i->i,这更短但只是隐藏了拆箱操作。

答案 2 :(得分:7)

另一种可以进行转换的方法是使用lambda:mapToInt(i -> i)。 是否应该使用lambda或方法引用将在here中详细讨论,但摘要是您应该使用您认为更具可读性的任何内容。

答案 3 :(得分:1)

如果问题是“我可以避免在从Stream<T>转换为IntStream时传递转换器吗?”一个可能的答案可能是“Java中没有办法使这种转换类型安全,并使其同时成为Stream接口的一部分”。

确实,在没有转换器的情况下将Stream<T>转换为IntStream的方法可能如下所示:

public interface Stream<T> {
    // other methods

    default IntStream mapToInt() {
        Stream<Integer> intStream = (Stream<Integer>)this;
        return intStream.mapToInt(Integer::intValue);
    }
}

所以它假设在Stream<Integer>上调用,并且在其他类型的流上会失败。但是因为流是惰性评估的,并且由于类型擦除(请记住Stream<T>是通用的)代码将在消耗流的地方失败,这可能远离mapToInt()调用。并且它将以极难找到问题根源的方式失败。

假设您有代码:

public class IntStreamTest {

    public static void main(String[] args) {
        IntStream intStream = produceIntStream();
        consumeIntStream(intStream);
    }

    private static IntStream produceIntStream() {
        Stream<String> stream = Arrays.asList("1", "2", "3").stream();
        return mapToInt(stream);
    }

    public static <T> IntStream mapToInt(Stream<T> stream) {
        Stream<Integer> intStream = (Stream<Integer>)stream;
        return intStream.mapToInt(Integer::intValue);
    }

    private static void consumeIntStream(IntStream intStream) {
        intStream.filter(i -> i >= 2)
                .forEach(System.out::println);
    }
}

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)

有了这个堆栈跟踪,您是否能够快速确定问题出在produceIntStream()中,因为在错误类型的流上调用了mapToInt()

当然可以编写类型安全的转换方法,因为它接受具体的Stream<Integer>

public static IntStream mapToInt(Stream<Integer> stream) {
    return stream.mapToInt(Integer::intValue);
}

// usage
IntStream intStream = mapToInt(Arrays.asList(1, 2, 3).stream())

但它不太方便,因为它打破了流的流畅界面性质。

<强>顺便说一句:

Kotlin的扩展函数允许调用一些代码,因为它是类接口的一部分。因此,您可以将此类型安全方法称为Stream<java.lang.Integer>方法:

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