Java 8 Streams:为什么mapToInt需要Integer :: parseInt作为参数?

时间:2019-07-06 11:14:28

标签: java java-8 java-stream

在下一个示例中,我试图了解一些内容:

    Stream.of("a1", "a2", "a3")
            .map(s -> s.substring(1))
            .mapToInt(Integer::parseInt)
            .max()

     . (etc.)

为什么mapToInt需要

    Integer::parseInt 

作为参数?它不应该是隐式的吗?这个参数不是多余的吗?

谢谢!

6 个答案:

答案 0 :(得分:2)

这不是多余的。您需要说一下如何将字符串“ 1”转换(例如)为整数1。

(我想您可能会说无参数的mapToLong方法会很方便,但是设计人员没有在API中包含一个。)


我想您可能还会问为什么要去进行显式整数转换的麻烦。

如果您尝试将Stream::max应用于数字表示的字符串流,则会得到词法上的最大值,而不是数字上的最大值。例如,“ 9”大于“ 11”。

请注意,Stream::max方法没有没有参数的重载。您需要提供一个比较器;例如

Stream.of("a1", "a2", "a3")
    .map(s -> s.substring(1))
    .max(compareAsIntegers)

其中

Comparator<String> compareAsIntegers = new Comparator<String>() {
    public int compare (String s1, String s2) {
        return Integer.compare(Integer.parseInt(s1),
                               Integer.parseInt(s2));
    }
}

答案 1 :(得分:1)

substring返回的字符串不是整数。 mapToInt将对象流(在本例中为Strings)转换为int原语的IntStream。但是,它不知道如何进行映射,您需要为其提供一个函数,在您的情况下为parseInt

答案 2 :(得分:1)

您正在执行的操作如下:

Stream.of("a1", "a2", "a3")           //step1
        .map(s -> s.substring(1))     //step2
        .mapToInt(Integer::parseInt)  //step3
        .max()

您定义了一个字符串流(在步骤1中),然后将其取舍,将其上的1个字符丢弃,留下了“ 1”,“ 2”和“ 3”的流(步骤2),但请注意,仍然是字符串对象... 然后将其转换为整数,因此需要给'mapToInt'一个“函数”,该函数以字符串作为参数并返回整数(第3步)

并且在Integer类中定义:

https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#parseInt(java.lang.String)

您可以肯定地编写并传递自己的函数...但是为什么要重新发明轮子呢? :)

答案 3 :(得分:1)

重要的是要区分调用Stream#mapToInt(ToIntFunction)和参数ToIntFunction的作用。

  • mapToInt的调用是流将要做什么(即,将元素映射到int)。
  • ToIntFunction参数是如何如何将流映射到每个元素到int

他们是否可以包含一个无参数的mapToInt方法,该方法将String隐式地解析为int s?是的,但是look at how well that works for Stream#sorted() —这种情况远比无参数的mapToInt方法更加随意或模棱两可。

no-arg sorted方法假定元素为Comparable,这是Java中基本的,标准化的且广泛使用的接口-任何类都可以实现该接口(而只有一个类可以实现是String)。因此,尽管该方法不是类型安全的,但可以认为用例足够普遍,足以证明其存在的合理性。

但是,无参数的mapToInt方法假定元素是String并将其内容解析为int是一个非常特定的用例。为什么这样的通用API提供这样的特定操作?没有合理的理由,这将是一个完全武断的决定。例如,为什么不将每个String映射到其length?为什么String被特别处理而不是其他一些类型?因此,该方法接受一个ToIntFunction参数,该参数描述了如何视情况将元素映射到int的情况。


请注意,Integer::parseIntmethod reference,这是琐碎的lambda expression的缩写。两者都是functional interface的实现,与使用anonymous class相比,该选项的详细程度较低。换句话说,您正在创建一个新的ToIntFunction实例并将其作为参数传递。

以下所有功能均等效:

// method reference
stream.mapToInt(Integer::parseInt)

// lambda expression
stream.mapToInt((String value) -> Integer.parseInt(value)) // explicit parameter types
stream.mapToInt(value -> Integer.parseInt(value))          // implicit parameter types

// anonymous class
stream.mapToInt(new ToIntFunction<>() {

    @Override
    public int applyAsInt(String value) {
        return Integer.parseInt(value);
    }

})

streamStream<String>的地方。

答案 4 :(得分:0)

实际上,在阅读了这里的善意答案后,我明白了 1.我的问题还不够清楚。 2.答案...

所以

  1. 我真正的意思是-是否没有将Integer :: parseInt用作mapToInt冗余的参数?因为我觉得mapToInt恰好描述了该方法的目的-将对象映射到int,并且添加parseInt是不必要的,因为它实际上是相同的-将对象解析为int ...

  2. 但是后来我尝试也传递Integer:valueOf,它也被接受-因此我知道有几个选项可以传递给mapToInt,这回答了我的问题...

    < / li>

答案 5 :(得分:0)

1)mapToInt 函数的主要目的是将当前流转换为 Intstream,在 Intstream 上可以执行其他 Integer 操作,如 sum、Min、max 等。

  1. 它是泛型,你永远无法预测输入是什么,在你的情况下它是字符串,它可能是 double ,long 或其他东西。 由开发人员来处理显式转换。

  2. 您可以编写自己的函数,在本例中它是 function ,它只不过是作为 mapToInt func 参数的 ToIntFunction。 你可以重写你自己的函数式接口,比如 function ,但这里你也需要在函数 applyAsInt 里面做 parseInt,但这将是一次性的工作,你可以在进行某些其他类似调用时重用。