Convert Stream to IntStream

只愿长相守 提交于 2021-01-20 21:54:05

问题


I have a feeling I'm missing something here. I found myself doing the following

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

My problem is with the silly conversion from Stream to IntStream via the mapToInt(Integer::intValue)

Is there a better way of doing the conversion? all this is to avoid using max() from Stream, which requires passing a Comparator but the question is specifically on the convertion of Stream to IntStream


回答1:


Due to type erasure, the Stream implementation has no knowledge about the type of its elements and can’t provide you with neither, a simplified max operation nor a conversion to IntStream method.

In both cases it requires a function, a Comparator or a ToIntFunction, respectively, to perform the operation using the unknown reference type of the Stream’s elements.

The simplest form for the operation you want to perform is

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

given the fact that the natural order comparator is implemented as a singleton. So it’s the only comparator which offers the chance of being recognized by the Stream implementation if there is any optimization regarding Comparable elements. If there’s no such optimization, it will still be the variant with the lowest memory footprint due to its singleton nature.

If you insist on doing a conversion of the Stream to an IntStream there is no way around providing a ToIntFunction and there is no predefined singleton for a Number::intValue kind of function, so using Integer::intValue is already the best choice. You could write i->i instead, which is shorter but just hiding the unboxing operation then.




回答2:


I realize you are trying to avoid a comparator, but you could use the built-in for this by referring to Integer.compareTo:

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

Or as @fge suggests, using ::compare:

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



回答3:


Another way you could do the conversion is with a lambda: mapToInt(i -> i). Whether you should use a lambda or a method reference is discussed in detail here, but the summary is that you should use whichever you find more readable.




回答4:


If the question is "Can I avoid passing converter while converting from Stream<T> to IntStream?" one possible answer might be "There is no way in Java to make such conversion type-safe and make it part of the Stream interface at the same time".

Indeed method which converts Stream<T> to IntStream without a converter might be looked like this:

public interface Stream<T> {
    // other methods

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

So it suppose to be called on Stream<Integer> and will fail on other types of streams. But because streams are lazy evaluated and because of the type erasure (remember that Stream<T> is generic) code will fail at the place where stream is consumed which might be far from the mapToInt() call. And it will fail in a way that is extremely difficult to locate source of the problem.

Suppose you have code:

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

It will fail on consumeIntStream() call with:

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)

Having this stacktrace do you able to quickly identify that the problem is in produceIntStream() because mapToInt() was called on the stream of the wrong type?

Of course one can write converting method which is type safe because it accepts concrete Stream<Integer>:

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

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

but it's not very convenient because it breaks fluent interface nature of the streams.

BTW:

Kotlin's extension functions allow to call some code as it is a part of the class' interface. So you are able to call this type-safe method as a Stream<java.lang.Integer>'s method:

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


来源:https://stackoverflow.com/questions/28015278/convert-stream-to-intstream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!