我有一种感觉,我在这里遗漏了一些东西。我发现自己在做以下事情
private static int getHighestValue(Map<Character, Integer> countMap) {
return countMap.values().stream().mapToInt(Integer::intValue).max().getAsInt();
}
我的问题是通过Stream
IntStream
到mapToInt(Integer::intValue)
的愚蠢转换
有没有更好的转换方式?这一切都是为了避免使用max()
中的Stream
,这需要传递Comparator
,但问题是关于Stream
转换为IntStream
答案 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
方法
在这两种情况下,它分别需要一个函数Comparator
或ToIntFunction
来使用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) }
}