Scala 集合入门
1. 数组
1.1 定长数组
scala.Array 是定长的可变的索引型集合, JVM 中, Scala 的 Array 是以 Java 数组方式实现. String 对应 java.lang.String, Int, Double或其他与 Java 中基本类型对应数组都有基本类型数组, 比如说 Array(1, 2) 在 JVM 中就是一个 int[].
val arr1 = new Array[Int](10)
val arr2 = Array(1, 3) // 调用apply方法
arr2(1) // 3, apply 方法
arr2(1) = 2 // 可以改变 Array 中的值 , update 方法
1.2 变长数组
scala.collection.mutable.ArrayBuffer 是变长的可变的索引型集合, 在 ArrayBuffer 的尾部添加和删除元素是一个高效的操作, 你也可以在任何位置插入或者移除元素, 但是这样的操作并不那么高效
import scala.collection.mutable.ArrayBuffer
val b = ArrayBuffer[Int]() // 或 val b = ArrayBuffer[Int]
b += 1 // += 在尾部添加元素 1
b += (2 ,3) // += 尾部添加多个元素, 用括号括起来 1, 2, 3
b ++= Array(4, 5) // ++= 用来追加任何集合 1 2 3 4 5
b.trimEnd(2) // 删除尾部2个元素; 1 2 3
b.trimStart(1) // 删除头部2个元素 2 3
b.insert(1, 4) // 在下标1之前插入4; 2 4 3
b.insert(1, 6, 7) // 在下标1之前插入6, 7; 2 6 7 4 3
b.remove(2) // 删除 b(2); 2 6 4 3
b.remove(2, 2) // 删除 b.slice(2, 2+2); 2 6
如果你需要一个 Array, 但是不知道最终要装多少个元素, 你可以向使用 ArrayBuffer, 然后调用 b.toArray; 反过来你也可以使用 toBuffer 将数组转换为 ArrayBuffer
\(\color {red} {变长数组在 for 循环中有删除操作引发的不良后果}\)
scala> val ab = ArrayBuffer(1,2,3,4)
ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
scala> for (i<-0 until ab.size){if (ab(i) <3) ab.remove(i);println(ab)}
ArrayBuffer(2, 3, 4)
ArrayBuffer(2, 3, 4)
ArrayBuffer(2, 3, 4)
error: java.lang.IndexOutOfBoundsException: 3
1.3 遍历数组与数组转化
遍历数组
for ( i <- 0 until a.length)
for (i <- (0 until a.length).reverse)
for (elem <- a)
数组转换
利用 for (...) yield 循环会创建 一个类型和原始集合相同的集合的特点
val res = for(elem <- a if elem % 2 == 1) yield 2 * elem
上面的写法等价于
val res = a.filter(_ % 2 == 1).map(2 *_)
这里只是展示下 for 推导式在集合上的应用, 实际上, 在 Scala 中很少使用这种显示的循环结构, 更多的是利用函数来解决问题, 比如上面用到的 map 和 filter
1. 4 常用方法
val a = Array(1, 4, 3) // 或 val a = ArrayBuffer(1, 4, 3)
a.sum // 8
a.max // 4
a.sortWith(_<_) // 1 3 4, 返回一个新集合, a 并没有改变
scala.util.Sorting.quickSort(a)
下面看一下 toString 和 mkString方法
mkString 方法允许你指定元素之间的分隔符, 该方法的两个重载版本还可以指定前缀和后缀
val a = Array(2, 3)
val b = ArrayBuffer(2, 3)
scala> a.toString
res52: String = [I@52fa6742
scala> b.toString
res53: String = ArrayBuffer(2, 3)
scala> a.mkString
res60: String = 23
scala> a.mkString(" and ")
res61: String = 2 and 3
scala> a.mkString("(", ",", ")")
res62: String = (2,3)
从上面可以看出来, 如果是 Array 的话, 可以使用 mkString 方法将 Array 的内容打印到控制台
1. 5 多维数组
在 Scala中, 多维数组通过数组数组来实现. 比如说 Double 的二维数组类型为 Array[Array[Double]]
要构造这样的数组可以使用 ofDim 方法:
val ndim = Array.ofDim[Double](3, 4)
访问其中元素:
ndim(row)(column)
也可以自己创建一个不规则的数组
val triangle = new Array[Array[Int]](4) // 4 是二维数组的行数
for (i <- 0 until triangle.length) {
triangle(i) = new Array[Int](i + 1)
}
2. 元组
元组是不同类型的值的组合, 这些不同类型值包裹在在括号 ( ) 中; 和数组和字符串的不同, 元组的组元从1开始, 而不是 0, 你可以使用 _1, _2, ...
访问其组元; 可以通过模式匹配快速提取元组中的值.
scala> val t = (1, 1.414, "jerry")
t: (Int, Double, String) = (1,1.414,jerry)
scala> t._1
res18: Int = 1
scala> val (first, second, third) = t
first: Int = 1
second: Double = 1.414
third: String = jerry
scala> val (first, _, _) = t // 如果只是需要使用第一个组元, 可以在不需要的位置使用 _
first: Int = 1
3. 映射
有句经典的程序员名言, "如果只能有一种数据结构就用映射吧"
3.1 构造映射
Scala中, 映射是对偶的集合. 简单的说, 对偶就是2个值的元组, -> 操作符可以创建对偶, Scala会将 A->B 隐式转换为2元组 (A,B). 使用 -> 操作符只是因为 -> 更加符合人们对映射的直观感.
Scala 默认选择不可变映射, scala.collecton.immutable.Map, 如果你想要一个可变映射, 使用 scala.collecton.mutable.Map; 如果你想从一个空映射开始, 你需要指定映射的实现以及类型参数(可变与不可变都不可以直接 new Map[k, v]).
scala> val iage = Map("tom"->3, "jerry"->4)
iage: scala.collection.immutable.Map[String,Int] = Map(tom -> 3, jerry -> 4)
scala> val age = scala.collection.mutable.Map(("tom",3), ("jerry"->4))
age: scala.collection.mutable.Map[String,Int] = Map(tom -> 3, jerry -> 4)
scala> val m1 = new Map[Int, Int]
<console>:15: error: trait Map is abstract; cannot be instantiated
scala> val m2 = new scala.collection.mutable.HashMap[Int, Int]
m2: scala.collection.mutable.HashMap[Int,Int] = Map()
已排序映射
在操作映射时, 你需要指定一个实现, 哈希表或者一个平衡树, 默认是哈希映射. 如果你不仅有查找需求还有排序需求的话, 可以使用树形映射; Scala 2.12 中不可变树形映射和可变树形映射都有.
scala> val age = scala.collection.mutable.SortedMap("tom"->3, "jerry"->4)
age: scala.collection.mutable.SortedMap[String,Int] = TreeMap(jerry -> 4, tom -> 3)
scala> val iage = scala.collection.immutable.SortedMap("tom"->3, "jerry"->4)
iage: scala.collection.immutable.SortedMap[String,Int] = Map(jerry -> 4, tom -> 3)
3.2 获取映射中的值
获取映射值: 映射通过 () 表示法调用 apply 方法来查找某个键对应的值; 如果映射中不包含请求中使用的键, 会抛出异常, 可以通过 contains 方法检查映射是否包含某个键; 也可以使用 getOrElse 方法和 get 方法, get 方法返回一个 Option对象.
val tom = age("tom") // 3
val kitty = age.getOrElse("kitty", -1) // -1, 获取键对应值, 若键不存在, 返回传入值
val jerry = age.get("jerry") // jerry
3.3 更新映射
在可变映射中, 你可以更新某个映射的值, 或者添加一个新的对偶, 方法是 映射(key) = value
, 这会调用 update 方法; 如果 key 在映射中则更新, 不在映射中则添加;
对于不可变映射, 虽然你不能改变一个不可变映射, 不过你可以通过不可变映射创建一个新映射, 更新和添加对偶, 新映射也是不可变集合, 因为新映射和老的映射共享大部分结构, 这样创建新映射的效率会很高.
// 可变映射
age("tom") = 4 // age.update("tom", 4)
// 更新, 输出 scala.collection.mutable.Map[String,Int] = Map(tom -> 4, jerry -> 4)
age("kitty") = 5
// 添加, 输出 scala.collection.mutable.Map[String,Int] = Map(tom -> 4, kitty -> 5, jerry -> 4)
// 不可变映射
// 更新, 新映射也是不变的
var newAge = iage + ("tom"->64)
// 输出 scala.collection.immutable.Map[String,Int] = Map(tom -> 64, jerry -> 4)
// 添加, 输出也可以看出来 iage 并没有改变, tom ->3,
var newAge: scala.collection.mutable.Map[String,Int] = iage + ("kitty"->6)
// 输出newAge: scala.collection.immutable.Map[String,Int] = Map(tom -> 3, jerry -> 4, kitty -> 6)
对于可变映射, 可以通过 += 操作符添加多个对偶, 要移除某个对偶使用 -=;
age += ("snow" -> 1, "dneyis"->3)
age -= "tom" // 即使映射中没有 tom, 也不会报错
3.4 遍历映射
for ((k, v) <- 映射) yield (k, v)
for ((k, v) <- 映射) yield (v, k) // 交换键和值的位置
这里通过模式匹配迭代获取映射中的对偶, 从而遍历了映射; Scala 中提供了方法来访问键和值
scala> age.keyS
keySet keys keysIterator
scala> age.values
values valuesIterator
3.5 拉链操作
使用元组原因之一就是把多个值绑在一起, 以便它们能一起处理, 这通常用 zip 方法来完成. 比如说
val name = Array("tom", "jerry")
val age = Array(3 , 4)
val pairs = name.zip(age) // pairs: Array[(String, Int)] = Array((tom,3), (jerry,4))
如果你有一个键的集合, 以及一个与之平行对应的值的集合, 那么你就可以用拉链操作将它们组合成一个映射了, 然后通过 Array 的 toMap 方法对偶的集合转换成映射, 最后结合 for ((k, v) <- 映射) yield statements
, 这些对偶就可以被一起处理了
keys.zip(values).toMap
for ((k, v) <- 映射) yield statements
4. 排序
全部排序
下面引用 Scala 使用指南的例子说说集合该如何排序.
特质 Ordering 及其 on方法定义如下
trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable
on[U](f: (U) ⇒ T): Ordering[U]
Given f, a function from U into T, creates an Ordering[U] whose compare function is equivalent to:
def compare(x:U, y:U) = Ordering[T].compare(f(x), f(y))
Ordering 特质的伴生对象 Ordering 及其 by 方法定义如下
object Ordering extends LowPriorityOrderingImplicits with Serializable
def by[T, S](f: (T) ⇒ S)(implicit ord: Ordering[S]): Ordering[T]
Given f, a function from T into S, creates an Ordering[T] whose compare function is equivalent to:
def compare(x:T, y:T) = Ordering[S].compare(f(x), f(y))
This function is an analogue(类似物) to Ordering.on where the Ordering[S] parameter is passed implicitly.
on[U](f: (U) ⇒ T)
首先特质定义中 Ordering[T] 的 T 表明该特质用于 T 类型数据的排序. on 方法定义表明输入为 U 数据类型, 而 Ordering[T] 只能对 T 类型数据进行排序, 所以 on 方法接收 f: (U) ⇒ T 的函数, 然后对函数输出的 T 数据类型进行排序.
by[T, S](f: (T) ⇒ S)(implicit ord: Ordering[S])
Ordering 对象的 by 方法定义表明输入为 T 数据类型, 但是却要对 S 型数据排序, 所以需要能对 S 型数据排序的 Ordering[S], 故需要传入能对 S 型数据进行排序的Ordering[S]. 所以 by 方法接收 f: (T) ⇒ S 的函数, 然后对函数输出的 S 数据类型进行排序.
由于 Scala 具有自动类型推断的能力, 所以有时我们可以省略类型.下面看看例子吧
例子
import scala.util.Sorting
val pairs = Array(("a", 5, 2), ("c", 3, 1), ("b", 1, 3))
// sort by 2nd element, [(String, Int, Int), Int] 是函数类型
// Ordering 为对象
Sorting.quickSort(pairs)(Ordering.by[(String, Int, Int), Int](_._2))
// sort by the 3rd element, then 1st
// Ordering[(Int, String)]为特质, 不省略类型即为
// Sorting.quickSort(pairs)(Ordering[(Int, String)].on[(String, Int, Int)](x => (x._3, x._1)))
Sorting.quickSort(pairs)(Ordering[(Int, String)].on(x => (x._3, x._1)))
最大值
// 默认按照第一个排序
def max: A
// 可以选择排序的元素
def maxBy[B](f: (A) ⇒ B): A
举个例子
scala> val arr = Array(("a", 5, 2), ("c", 2, 3), ("b", 3, 3))
scala> arr.max
res43: (String, Int, Int) = (c,2,3)
scala> arr.maxBy(_._2)
res44: (String, Int, Int) = (a,5,2)
scala> arr.maxBy(_._3)
res45: (String, Int, Int) = (c,2,3)
scala> arr.maxBy(x=>(x._3,x._2))
res47: (String, Int, Int) = (b,3,3)
Scala 集合入门的更多相关文章
- Scala快速入门 - 基础语法篇
本篇文章首发于头条号Scala快速入门 - 基础语法篇,欢迎关注我的头条号和微信公众号"大数据技术和人工智能"(微信搜索bigdata_ai_tech)获取更多干货,也欢迎关注我的 ...
- Scala集合操作
大数据技术是数据的集合以及对数据集合的操作技术的统称,具体来说: 1.数据集合:会涉及数据的搜集.存储等,搜集会有很多技术,存储技术现在比较经典方案是使用Hadoop,不过也很多方案采用Kafka. ...
- Scala 基础入门【翻译】
原文地址 本文只是带你进入 Scala 的世界,包括安装.不可变量 val.可变量 var.定义类.集合(包括列表(list).集(set).映射(map))以及集合遍历和集合库(能达到并行/并发效果 ...
- Scala 快速入门
 Scalable 编程语言 纯正的的面向对象语言 函数式编程语言 无缝的java互操作 scala之父 Martin Odersky 1. 函数式编程 函数式编程(functional progr ...
- Spark:scala集合转化为DS/DF
scala集合转化为DS/DF case class TestPerson(name: String, age: Long, salary: Double) val tom = TestPerson( ...
- Scala集合常用方法解析
Java 集合 : 数据的容器,可以在内部容纳数据 List : 有序,可重复的 Set : 无序,不可重复 Map : 无序,存储K-V键值对,key不可重复 scala 集合 : 可变集合( ...
- Scala集合笔记
Scala的集合框架类比Java提供了更多的一些方便的api,使得使用scala编程时代码变得非常精简,尤其是在Spark中,很多功能都是由scala的这些api构成的,所以,了解这些方法的使用,将更 ...
- Scala集合(一)
Scala集合的主要特质 Iterator,用来访问集合中所有元素 val coll = ... // 某种Iterable val iter = col.iterator while(iter.ha ...
- Scala集合类型详解
Scala集合 Scala提供了一套很好的集合实现,提供了一些集合类型的抽象. Scala 集合分为可变的和不可变的集合. 可变集合可以在适当的地方被更新或扩展.这意味着你可以修改,添加,移除一个集合 ...
随机推荐
- 【CF932G】Palindrome Partition(回文树,动态规划)
[CF932G]Palindrome Partition(回文树,动态规划) 题面 CF 翻译: 给定一个串,把串分为偶数段 假设分为了\(s1,s2,s3....sk\) 求,满足\(s_1=s_k ...
- 【BZOJ4407】于神之怒加强版(莫比乌斯反演)
[BZOJ4407]于神之怒加强版(莫比乌斯反演) 题面 BZOJ 求: \[\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)^k\] 题解 根据惯用套路 把公约数提出来 \[\sum ...
- [BZOJ2048] [2009国家集训队] 书堆
Description Input 第一行正整数 N M Output 一行(有换行符),L,表示水平延伸最远的整数距离 (不大于答案的最大整数) Sample Input #11 100 #22 1 ...
- STM32高级定时器TIM1产生两路互补的PWM波(带死区)
测试环境:Keil 5.20.0.0 STM32F103RBT6 固件库版本:STM32F10x_StdPeriph_Lib_V3.5.0(2011) 本文使用TIM1的通道1,通道2,产生两路1kh ...
- Android KeyCode 列表
基本按键 KEYCODE_0 按键'0' 7 KEYCODE_1 按键'1' 8 KEYCODE_2 按键'2' 9 KEYCODE_3 按键'3' 10 KEYCODE_4 按键'4' 11 KEY ...
- postgresql数据操作
windows下操作postgresql 删除数据库:dropdb.exe -U postgres 数据库名称 创建数据库createdb.exe -U postgres lilei_db1--lil ...
- 方法的重写与重载的区别(Override与Overload)。重载的方法是否可以改变返回值的类型
方法的重写(Override)与重载(Overload)的区别.重载的方法是否可以改变返回值的类型?[基础] 解释: 方法的重写overriding和重载Overloading是Java多态性的不同表 ...
- Intel 移位指令的陷阱(转)
今天发现了一个Intel逻辑左移指令shl的一个bug. 逻辑左移的概念是对给定的目的操作数左移COUNT次,每次移位时最高位移入标志位CF中,最低位补零. 其中OPRD1为目的操作数, 可以是通 ...
- c++趣味之shared_ptr额外好处
shared_ptr(sp)额外好处是什么?即使被转为基类,析构函数也可以正常执行. 已知两个类 class foo{}; class bar:foo{public:~bar(){}}; 先来看不用s ...
- 提高UI设计效率的4个技巧
提高UI设计效率的4个技巧 如何提高UI设计的速度?在这里分享一些我观察到的常见问题和改善方式.当然,需要注意的地方何其多. 身为设计师的你,应该要了解工程实作的基本原理: 业界 NG 率:接近 10 ...