将流转换为 IntStream
Convert Stream to IntStream
我觉得我在这里遗漏了一些东西。我发现自己在做以下事情
private static int getHighestValue(Map<Character, Integer> countMap) {
return countMap.values().stream().mapToInt(Integer::intValue).max().getAsInt();
}
我的问题是通过 mapToInt(Integer::intValue)
从 Stream
到 IntStream
的愚蠢转换
有没有更好的转换方法?所有这一切都是为了避免使用 Stream
中的 max()
,这需要传递 Comparator
,但问题具体在于 Stream
到 IntStream
[=19 的转换=]
我知道你试图避免使用比较器,但你可以通过参考 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();
}
另一种可以进行转换的方法是使用 lambda:mapToInt(i -> i)
。
详细讨论了您应该使用 lambda 还是方法引用 here,但总结是您应该使用您认为更易读的那个。
由于类型擦除,Stream
实现不知道其元素的类型,因此无法为您提供简化的 max
操作或到 [=13 的转换=]方法。
在这两种情况下,它都需要一个函数,分别是 Comparator
或 ToIntFunction
,以使用 Stream
元素的未知引用类型执行操作。
您要执行的操作的最简单形式是
return countMap.values().stream().max(Comparator.naturalOrder()).get();
考虑到 自然顺序 比较器是作为单例实现的。因此,它是唯一有机会被 Stream
实现识别的比较器 如果 有关于 Comparable
元素的任何优化。如果没有这样的优化,由于它的单例特性,它仍然是内存占用最少的变体。
如果您坚持将 Stream
转换为 IntStream
,则无法提供 ToIntFunction
,并且 [=22= 没有预定义的单例] 之类的功能,所以使用 Integer::intValue
已经是最好的选择了。您可以改写 i->i
,它更短但只是隐藏了拆箱操作。
如果问题是 "Can I avoid passing converter while converting from Stream<T>
to IntStream
?",一个可能的答案可能是 "There is no way in Java to make such conversion type-safe and make it part of the Stream
interface at the same time"。
在没有转换器的情况下将 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.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 的扩展函数允许调用一些代码,因为它是 class' 接口的一部分。所以你可以调用这个类型安全的方法作为 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) }
}
我觉得我在这里遗漏了一些东西。我发现自己在做以下事情
private static int getHighestValue(Map<Character, Integer> countMap) {
return countMap.values().stream().mapToInt(Integer::intValue).max().getAsInt();
}
我的问题是通过 mapToInt(Integer::intValue)
Stream
到 IntStream
的愚蠢转换
有没有更好的转换方法?所有这一切都是为了避免使用 Stream
中的 max()
,这需要传递 Comparator
,但问题具体在于 Stream
到 IntStream
[=19 的转换=]
我知道你试图避免使用比较器,但你可以通过参考 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();
}
另一种可以进行转换的方法是使用 lambda:mapToInt(i -> i)
。
详细讨论了您应该使用 lambda 还是方法引用 here,但总结是您应该使用您认为更易读的那个。
由于类型擦除,Stream
实现不知道其元素的类型,因此无法为您提供简化的 max
操作或到 [=13 的转换=]方法。
在这两种情况下,它都需要一个函数,分别是 Comparator
或 ToIntFunction
,以使用 Stream
元素的未知引用类型执行操作。
您要执行的操作的最简单形式是
return countMap.values().stream().max(Comparator.naturalOrder()).get();
考虑到 自然顺序 比较器是作为单例实现的。因此,它是唯一有机会被 Stream
实现识别的比较器 如果 有关于 Comparable
元素的任何优化。如果没有这样的优化,由于它的单例特性,它仍然是内存占用最少的变体。
如果您坚持将 Stream
转换为 IntStream
,则无法提供 ToIntFunction
,并且 [=22= 没有预定义的单例] 之类的功能,所以使用 Integer::intValue
已经是最好的选择了。您可以改写 i->i
,它更短但只是隐藏了拆箱操作。
如果问题是 "Can I avoid passing converter while converting from Stream<T>
to IntStream
?",一个可能的答案可能是 "There is no way in Java to make such conversion type-safe and make it part of the Stream
interface at the same time"。
在没有转换器的情况下将 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.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 的扩展函数允许调用一些代码,因为它是 class' 接口的一部分。所以你可以调用这个类型安全的方法作为 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) }
}