Scala集合类上的高阶方法
Scala集合的真正强大之处在于带来了其高阶方法。一个高阶方法使用一个函数作为其输入参数。需要特别注意的是,一个高阶方法并不改变集合。下面是Scala集合的一些最主要的高阶方法。
1、map
Scala 集合的map 方法将其输入函数应用到集合中所有元素上,并返回另一个集合。返回的集合具有与调用map 方法的那个集合相同数量的元素。不过,在返回的集合中的元素并不是原始集合中相同的类型。示例如下:
val xs = List(1,2,3,4); val ys = xs.map((x:Int) => x*10.0);
在上面的代码中,xs 的类型是List[Int],而ys 的类型是List[Double]。
如果一个函数只有一个参数,那么圆括号可以被花括号替换。下面的两个语句等价:
val ys = xs.map((x:Int) => x*10.0); val ys = xs.map{(x:Int) => x*10.0};
正如前面讲过的,Scala 允许使用运算符标记调用任何方法。要进一步提高可读性,前面的代码也可以写成下面这样:
val ys = xs map {(x:Int) => x*10.0};
Scala 可以从集合的类型推断出传入的参数类型,因此可以忽略掉参数类型。下面两个语句是等价的:
val ys = xs map {(x:Int) => x*10.0}; val ys = xs map {x => x*10.0};
如果一个函数字面量的输入参数只在函数体中使用一次,那么右箭头和该箭头的左侧可以从函数字面量中被删除。我们可以只编写函数字面量的函数体。下面的两个语句是等价的:
val ys = xs map {x => x*10.0}; val ys = xs map {_ * 10.0};
上面代码中,下划线(_)字符代表函数字面量的输入传给map 方法。上面的代码可以被理解为集合xs 中的每个元素乘以10。 总结来说,下面的代码分别代表了相同语句的冗长的版本和简洁的版本:
val ys = xs.map((x:Int) => x*10.0); val ys = xs map {_ * 10.0};
下面是使用map方法的另一个示例:
// 快速产生0.1, 0.2, 0.3等方式的数字 (1 to 9).map(0.1 * _).foreach(println(_)) // 打印三角形 (1 to 9).map("*" * _).foreach(println(_))
2、flatMap
Scala 集合的flatMap将集合中的所有集合元素展开,并形成单个集合。flatMap 方法类似于map。它接收一个函数作为输入,并将该输入函数应用到集合中的每个元素,并返回另一个集合作为结果。不过,传递给flatMap 的函数为原始集合中的每个元素都生成一个集合。因此,应用该输入函数的结果是一个集合的集合。如果相同的输入函数被传递给map 方法,它将返回一个集合的集合。而flatMap 方法则返回一个flattened集合。
下面的例子描述了flatMap 的使用:
val numbersList = List(List(1,2,3), List(4,5,6), List(7,8,9)) numbersList.flatMap(list => list)
输出结果如下:
res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
请看下面的代码:
val line = "Scala is fun"; val SingleSpace = " "; val words = line.split(SingleSpace); val arrayOfChars = words flatMap {_.toList};
输出结果如下:
arrayOfChars: Array[Char] = Array(S, c, a, l, a, i, s, f, u, n)
集合的toList 方法创建集合中所有元素的一个列表。对于将一个字符串、数组、set、或任何其它集合类型转换为一个list,该方法很有用。
3、filter
方法filter 应用一个断言到集合中的每个元素,并返回另一个集合(由断言返回true 的元素组成)。一个断言是返回一个Boolean 值的函数。它返回true 或false。
val xs = (1 to 100).toList; val even = xs filter {_%2 == 0};
使用filter方法,按照过滤条件,将原集合中不符合条件的数据过滤掉,输出所有匹配某个特定条件的元素,得到一个序列中的所有偶数:
(1 to 9).filter(line => line % 2 == 0).foreach(println(_)) (1 to 9).filter(_ % 2 ==0).foreach(println) // 与上一句等价
4、foreach
Scala 集合的foreach 方法在集合的每个元素上调用其输入函数,但并不返回任何东西。这类似于map方法。这两个方法间的唯一区别在于map 返回一个集合,而foreach 并不返回任何东西。由于它的副作用,这是一个很少使用的方法。
val words = "Scala is fun".split(" "); words.foreach(println);
5、reduce
方法reduce 返回单个值。正如其名所暗示的,它将一个集合归约为一个单个的值。方法reduce 的输入函数同时接收两个输入并返回一个值。本质上来说,输入函数是一个可组合和可交换的二元运算符。
请看下面的示例:
val xs = List(2,4,6,8,10); val sum = xs reduce {(x,y) => x+y}; val product = xs reduce {(x,y) => x*y}; val max = xs reduce {(x,y) => if(x > y) x else y}; val min = xs reduce {(x,y) => if(x < y) x else y};
下面这个示例找出一个语句中最长的单词:
val words = "Scala is fun".split(" "); val longestWord = words reduce {(w1,w2) => if(w1.length > w2.length) w1 else w2};
reduce和reduceLeft都是从左边的操作数开始,而reduceRight是从右边的操作数开始:
(1 to 9).reduce((v1:Int, v2:Int) => v1 + v2) (1 to 9).reduce(_ + _) (1 to 9).reduceLeft(_ + _) (1 to 9).reduceRight(_ + _)
6、sortWith
使用sortWith函数对集合进行排序:
(1 to 9).sortWith((v1:Int, v2:Int) => v1 > v2).foreach(println(_)) println("=======") (1 to 9).sortWith(_ > _).foreach(println)
【示例】单词计数程序。
下面是一个Scala版本的单词计数程序,其中演示了集合类上的高阶函数用法。
import scala.io.Source object WordCount { def main(args: Array[String]): Unit = { // 方式1 // val text = List("good good study","day day up") // val words = text.flatMap(_.split(" ")) // val tuples = words.map((_,1)) // val grouped = tuples.groupBy(_._1) // val sumed = grouped.mapValues(_.size) // val sorted = sumed.toList.sortBy(_._2) // val result = sorted.reverse // result.foreach(println) // 方式2 // val text = List("good good study","day day up") // text.flatMap(_.split(" ")) // .map((_,1)) // .groupBy(_._1) // .mapValues(_.size) //// .toList.sortBy(_._2) // .toList.sortWith(_._2 < _._2) //// .reverse // .foreach(println) // 方式3:简洁写法 val text = Source.fromFile("""word.txt""").getLines().toList text.flatMap(_.split(" ")) .map((_,1)) .groupBy(_._1) .mapValues(_.size) .toList.sortWith(_._2 < _._2) .foreach(println) } }