Scala中Seq的filterNot为何不支持List参数,Set却可以?
这问题我当初刚接触Scala集合的时候也踩过一模一样的坑!核心原因在于filterNot的参数要求,以及不同集合类型的apply方法行为差异。
先搞懂filterNot需要什么参数
filterNot方法的定义要求传入一个谓词函数,也就是签名为 T => Boolean 的函数——简单说就是给它一个集合元素,它返回布尔值,以此决定要不要过滤掉这个元素。比如我们常用的:
val a = Seq(1,2,3,4,5,6) a.filterNot(_ > 3) // 返回List(1,2,3)
这里_ > 3就是一个Int => Boolean的函数,完全符合要求。
为什么Set(1,2,3)能正常工作?
Scala的Set有一个非常实用的apply方法,它的签名是 def apply(elem: A): Boolean,作用就是判断传入的元素是否存在于Set中。当你直接把Set(1,2,3)传给filterNot时,Scala会自动把这个Set转换成对应的函数(相当于把元素传入Set的apply方法),等价于你写了:
a.filterNot(x => Set(1,2,3).contains(x))
这个匿名函数的类型是Int => Boolean,完美匹配filterNot的参数要求,所以能正常返回List(4,5,6)。
为什么Seq/List会报编译错误?
而Seq和List的apply方法完全是另一个逻辑:它的签名是 def apply(index: Int): A,作用是根据索引位置获取集合中的元素,而不是判断元素是否存在。当你写a.filterNot(Seq(1,2,3))时,Scala会尝试把这个Seq当作函数来使用——也就是把集合中的每个元素(比如第一个元素1)传入Seq的apply方法。
这时候就会出现两个问题:
- 首先,
apply需要的是索引(比如0、1、2),但你传入的是元素值1; - 其次,
apply返回的是对应索引的元素(比如Seq(1,2,3).apply(1)返回2,是Int类型),但filterNot需要的是Boolean类型的返回值。
这就是编译器报错<console>:13: error: type mismatch; found : Int(3) required: Boolean的原因——它期望得到布尔值,结果得到了整数。
用Seq/List实现预期效果的正确方式
如果你想用Seq或者List来过滤元素,只需要显式调用contains方法,把它转换成符合要求的谓词函数即可:
// 方式一:直接传递contains方法引用 a.filterNot(Seq(1,2,3).contains) // 方式二:写匿名函数 a.filterNot(x => List(1,2,3).contains(x))
这两种写法都会返回你预期的List(4,5,6)。
内容的提问来源于stack exchange,提问作者hahaha

