【Scala】集合操作大全

目录

 一、数组

不可变数组

 可变数组

 二维数组

二、列表

不可变列表(List):

可变列表(ListBuffer):

三、集合 

不可变集合:

可变集合:

四、 映射Map

不可变映射:

可变映射:


Java集合中的三大类型:

  • 序列 Seq
  • 集合Set
  • 映射Map

它们中有多种不同的实现:

对于几乎所有的集合类,Scala 都同时提供了可变和不可变的版本,分别位于以下两个包:
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable

  • 不可变集合,指该集合长度数量不可修改,每次修改(比如增删元素)都会返回一个新的对象,而不会修改源对象。
  • 可变集合可以对源对象任意修改,一般也提供不可变集合相同的返回新对象的方法,但也可以用其他方法修改源对象。
  • 建议:操作集合时,不可变用操作符,可变用方法。操作符也不一定就会返回新对象,但大多是这样的,还是要具体看。

不可变集合继承图

  • scala中的String就是java.lang.String,和集合无直接关系,所以是虚箭头,是通过Perdef中的低优先级隐式转换来做到的。经过隐式转换为一个包装类型后就可以当做集合了。
  • ArrayString类似,在图中漏掉了。
  • 此类包装为了兼容java在scala中非常常见,scala中很多类型就是对java类型的包装或者仅仅是别名。
  • scala中可能会推荐更多地使用不可变集合。能用不可变就用不可变。

 

 可变集合继承图

 

  • 序列中多了Buffer,整体结构差不多。

 

不可变和可变:

  • 不可变指的是对象大小不可变,但是可以修改元素的值(不能修改那创建了也没有用对吧),需要注意这一点。而如果用了val不变量存储,那么指向对象的地址也不可变。
  • 不可变集合在原集合上个插入删除数据是做不到的,只能返回新的集合。

 一、数组

不可变数组

  • 访问元素使用()运算符,通过apply/update方法实现,源码中的实现只是抛出错误作为存根方法(stab method),具体逻辑由编译器填充。
// 1. new
val arr = new Array[Int](5)//(5)表示限定不可变

// 2. factory method in companion obejct
val arr1 = Array[Int](5)
val arr2 = Array(0, 1, 3, 4)

// 3. traverse, range for 遍历访问数组元素
for (i <- 0 until arr.length) arr(i) = i
for (i <- arr.indices) print(s"${arr(i)} ")
println()

// 4. tarverse, foreach 直接遍历所有元素(增强for循环)
for (elem <- arr) print(s"$elem ") // elem is a val
println()

// 5. tarverse, use iterator 迭代器
val iter = arr.iterator
while (iter.hasNext)
    print(s"${iter.next()} ")
println()

// 6. traverse, use foreach method, pass a function 调用foreach方法
arr.foreach((elem: Int) => print(s"$elem "))
println()

println(arr2.mkString(", ")) // to string directly 输出:0,1,3,4

// 7. add element, return a new array, : should toward to object 
val newArr = arr :+ 10 //  arr.:+(10) add to end
println(newArr.mkString(", "))
val newArr2 = 20 +: 10 +: arr :+ 30 // arr.+:(10).+:(20).:+(30)
println(newArr2.mkString(", "))
//+:(数字) 该数字放最前;:+(数字)该数字放最后
  • 3.中的indices就等于0 until arr.length
  • 可以看到自定义运算符可以非常灵活,规定如果运算符首尾有:那么:一定要指向对象。
  • 下标越界当然会抛出异常,使用前应该检查。
  • 通过Predef中的隐式转换为一个混入了集合相关特征的包装类型从而得以使用scala的集合相关特征,Array类型中并没有相关混入。
  • 7.中添加元素,不是说直接改变这个数组,而是添加元素后,作为一个新的数组返回

 可变数组

// 1. create
val arr: ArrayBuffer[Int] = new ArrayBuffer[Int]()
val arr1: ArrayBuffer[Int] = ArrayBuffer(10, 20, 30)
println(arr.mkString(", "))
println(arr1) // call toString ArrayBuffer(10, 20, 30)

// 2. visit
arr1(2) = 10
// 3. add element to tail
var newArr = arr :+ 15 :+ 20 // do not change arr
println(newArr)
newArr = arr += 15 // modify arr itself, add to tail return itself, do notrecommand assign to other var
println(arr)
println(newArr == arr) // true
// 4. add to head
77 +=: arr // WTF?
println(arr)
// 5. insert to middle
arr.insert(1, 10)
println(arr)
// 6. remove element
arr.remove(0, 1) // startIndex, count
println(arr)
arr -= 15 // remove specific element
println(arr)
// 7. convert to Array
val newImmuArr: Array[Int] = arr.toArray
println(newImmuArr.mkString(", "))
// 8. Array to ArryBuffer
val buffer: scala.collection.mutable.Buffer[Int] = newImmuArr.toBuffer
println(buffer)
  • 推荐:不可变集合用运算符,可变集合直接调用对应方法。运算符容易迷惑。
  • 更多方法查看文档和源码用到去找就行。
  • 可变数组和不可变数组可以调用方法互相转换。

 二维数组

  • 就是数组的数组。
  • 使用Array.ofDim[Type](firstDim, secondDim, ...)方法。
// create 2d array
val arr: Array[Array[Int]] = Array.ofDim[Int](2, 3)
arr(0)(1) = 10
arr(1)(0) = 100
 
// traverse
arr.foreach(v => println(v.mkString(",")))

二、列表

不可变列表(List):

  • List,抽象类,不能直接new,使用伴生对象apply传入元素创建。
  • List本身也有apply能随机访问(做了优化),但是不能update更改。
  • foreach方法遍历。
  • 支持+: :+首尾添加元素。
  • Nil空列表,::添加元素到表头。
  • 常用Nil.::(elem)创建列表,换一种写法就是10 :: 20 :: 30 :: Nil得到结果List(10, 20, 30)
  • 合并两个列表:list1 ::: list2 或者list1 ++ list2
def main(args: Array[String]): Unit = {
    //1.create a list
    val list1=List(11,12,13)
    println(list1(1))
    //list(1)=12 NO way!!
    //2.遍历列表元素
    println(list1)
    list1.foreach(println)
    //3.添加元素
    val list2 = 10 +: list1
    val list3 = list1 :+ 23
    println(list1)//>>>List(11, 12, 13)
    println(list2)//>>>List(10, 11, 12, 13)
    println(list3)//>>>List(11, 12, 13, 23)

    println("===========================")
    val list4=list2.::(51)//放在开头
    println(list4)
    //4.创建新列表
    val list5 = Nil.::(13)
    println(list5)

    val list6=32::Nil
    val list7=17::28::58::16::Nil
    println(list6)//>>>List(32)
    println(list7)//>>>List(17, 28, 58, 16)

    //5.合并列表
    val list8 = list6 :: list7
    println(list8)//>>> List(List(32), 17, 28, 58, 16)

    //拆开再合并,放到一个list里面
    val list9 = list6 ::: list7
    println(list9)//>>>List(32, 17, 28, 58, 16)
    val list10 = list6 ++ list7
    println(list10)


  }

 

可变列表(ListBuffer):

  • 可变列表ListBuffer,和ArrayBuffer很像。
  • final的,可以直接new,也可以伴生对象apply传入元素创建(总体来说scala中更推荐这种方式)。
  • 方法:append prepend insert remove
  • 添加元素到头或尾:+=: +=
  • 合并:++得到新的列表,++=合并到源上。
  • 删除元素也可以用-=运算符。
  • 具体操作很多,使用时阅读文档即可。
import scala.collection.mutable.ListBuffer

object Test_ListBuffer {
  def main(args: Array[String]): Unit = {
    //1.创建可变列表
    val list1:ListBuffer[Int]=new ListBuffer[Int]()
    val list2=ListBuffer(12,53,75)
    println(list1)//>>>ListBuffer()
    println(list2)//>>>ListBuffer(12, 53, 75)
    //2.添加元素
    list1.append(11,12)
    list2.prepend(20)
    list1.insert(1,19,22)//在索引为1的位置添加19和22
    println(list1)//>>>ListBuffer(11, 19, 22, 12)
    println(list2)//>>>ListBuffer(20, 12, 53, 75)
    31 +=: 96 +=: list1 += 25 += 11//前面加两个后面加两个
    println(list1)
    //3.合并
    val list3=list1 ++ list2
    println(list3)

    list1 ++= list2
    println(list1)//>>>ListBuffer(31, 96, 11, 19, 22, 12, 25, 11, 20, 12, 53, 75)
    println(list2)//>>>ListBuffer(20, 12, 53, 75)
    //4.修改元素
    list2(3)=30
    list2.update(0,89)
    println(list2)//ListBuffer(89, 12, 53, 30)
    //5.删除元素
    list2.remove(2)
    list2 -= 53
    println(list2)//>>>ListBuffer(89, 12, 30)


  }
}

三、集合 

不可变集合(set):

  • 数据无序,不可重复。
  • 可变和不可变都叫Set,需要做区分。默认Set定义为immutable.Set别名。
  • 创建时重复数据会被去除,可用来去重。
  • 添加元素:set + elem
  • 合并:set1 ++ set2
  • 移除元素:set - elem
  • 不改变源集合。
    //1.创建set
    val set1 = Set(12,23,34,4,5,56,12,23)
    println(set1)
    //2.添加元素(无序添加)
    val set2 = set1 + 20
    println(set2)
    //3.合并
    val set3 = Set(19,13,23,53,67,79)
    val set4 = set2 ++ set3
    println(set4)
    //4.删除元素
    val set5 = set3-13
    println(set5)

可变集合:

  • 操作基于源集合做更改。
  • 为了与不可变集合区分,import scala.collection.mutable并用mutable.Set
  • 不可变集合有的都有。
  • 添加元素到源上:set += elem add
  • 删除元素:set -= elem remove
  • 合并:set1 ++= set2
  • 都很简单很好理解,多看文档和源码就行。
import scala.collection.mutable

object Test_MutableSet {
  //1.创建set
  def main(args: Array[String]): Unit = {
    val set1:mutable.Set[Int]=mutable.Set(13,23,53,12,13,23,78)
    println(set1)
    //2.添加元素
    val set2 =set1 + 11
    println(set2)
    set1 += 11
    println(set1)
    set1.add(10)
    println(set1)
    //3.删除元素
    set1 -=11
    val flag3=set1.remove(10)
    //4.合并集合
    val set4=set1 ++ set2
    set1 ++= set2
    println(set4)
    println(set1)


  }

}

 

四、 映射Map

不可变映射:

  • Map默认就是immutable.Map别名。
  • 两个泛型类型。
  • 基本元素是一个二元组。
// create Map
val map: Map[String, Int] = Map("a" -> 13, "b" -> 20)
println(map)
// traverse
map.foreach((kv: (String, Int)) => println(kv))
map.foreach(kv => println(s"${kv._1} : ${kv._2}"))
// get keys and values
for (key <- map.keys) {
    println(s"${key} : ${map.get(key)}")
}
// get value of given key
println(map.get("a").get)
println(map.getOrElse("c", -1)) // avoid excption
println(map("a")) // if no such key will throw exception
// merge
val map2 = map ++ Map("e" -> 1024)
println(map2)

可变映射:

  • mutable.Map
  • 不可变的都支持。
// create mutable Map
val map: mutable.Map[String, Int] = mutable.Map("a" -> 10, "b" -> 20)
// add element
map.put("c", 30)
map += (("d", 40)) // two () represent tuple to avoid ambiguity
println(map)
// remove element
map.remove("a")
map -= "b" // just need key
println(map)
// modify element
map.put("c", 100) // call update, add/modify
println(map)
// merge Map
map ++= Map("a" -> 10, "b" -> 20, "c" -> 30) // add and will override
println(map)

元组:

  • (elem1, elem2, ...) 类型可以不同。
  • 最多只能22个元素,从Tuple1定义到了Tuple22
  • 使用_1 _2 _3 ...访问。
  • 也可以使用productElement(index)访问,下标从0开始。
  • ->创建二元组。
  • 遍历:for(elem <- tuple.productIterator)
  • 可以嵌套,元组的元素也可以是元组。

 

集合通用属性和方法:

  • 线性序列才有长度length、所有集合类型都有大小size
  • 遍历for (elem <- collection)、迭代器for (elem <- collection.iterator)
  • 生成字符串toString mkString,像Array这种是隐式转换为scala集合的,toString是继承自java.lang.Object的,需要自行处理。
  • 是否包含元素contains

衍生集合的方式:

  • 获取集合的头元素head(元素)和剩下的尾tail(集合)。
  • 集合最后一个元素last(元素)和除去最后一个元素的初始数据init(集合)。
  • 反转reverse
  • 取前后n个元素take(n) takeRight(n)
  • 去掉前后n个元素drop(n) dropRight(n)
  • 交集intersect
  • 并集union,线性序列的话已废弃用concat连接。
  • 差集diff,得到属于自己、不属于传入参数的部分。
  • 拉链zip,得到两个集合对应位置元素组合起来构成二元组的集合,大小不匹配会丢掉其中一个集合不匹配的多余部分。
  • 滑窗sliding(n, step = 1),框住特定个数元素,方便移动和操作。得到迭代器,可以用来遍历,每个迭代的元素都是一个n个元素集合。步长大于1的话最后一个窗口元素数量可能个数会少一些。

集合的简单计算操作:

  • 求和sum 求乘积product 最小值min 最大值max
  • maxBy(func)支持传入一个函数获取元素并返回比较依据的值,比如元组默认就只会判断第一个元素,要根据第二个元素判断就返回第二个元素就行xxx.maxBy(_._2)
  • 排序sorted,默认从小到大排序。从大到小排序sorted(Ordering[Int].reverse)
  • 按元素排序sortBy(func),指定要用来做排序的字段。也可以再传一个隐式参数逆序sortBy(func)(Ordering[Int].reverse)
  • 自定义比较器sortWith(cmp),比如按元素升序排列sortWith((a, b) => a < b)或者sortWith(_ < _),按元组元素第二个元素升序sortWith(_._2 > _._2)
  • 例子:
object Calculations {
    def main(args: Array[String]): Unit = {
        // calculations of collections
        val list = List(1, 4, 5, 10)
        
        // sum
        var sum = 0
        for (elem <- list) sum += elem
        println(sum)
        
        println(list.sum)
        println(list.product)
        println(list.min)
        println(list.max)

        val list2 = List(('a', 1), ('b', 2), ('d', -3))
        println(list2.maxBy((tuple: (Char, Int)) => tuple._2))
        println(list2.minBy(_._2))

        // sort, default is ascending
        val sortedList = list.sorted
        println(sortedList)
        // descending
        println(list.sorted(Ordering[Int].reverse))

        // sortBy
        println(list2.sortBy(_._2))

        // sortWith
        println(list.sortWith((a, b) => a < b))
        println(list2.sortWith(_._2 > _._2))
    }
}

集合高级计算函数:

  • 大数据的处理核心就是映射(map)和规约(reduce)。
  • 映射操作(广义上的map):
    • 过滤:自定义过滤条件,filter(Elem => Boolean)
    • 转化/映射(狭义上的map):自定义映射函数,map(Elem => NewElem)
    • 扁平化(flatten):将集合中集合元素拆开,去掉里层集合,放到外层中来。flatten
    • 扁平化+映射:先映射,再扁平化,flatMap(Elem => NewElem)
    • 分组(group):指定分组规则,groupBy(Elem => Key)得到一个Map,key根据传入的函数运用于集合元素得到,value是对应元素的序列。
  • 规约操作(广义的reduce):
    • 简化/规约(狭义的reduce):对所有数据做一个处理,规约得到一个结果(比如连加连乘操作)。reduce((CurRes, NextElem) => NextRes),传入函数有两个参数,第一个参数是第一个元素(第一次运算)和上一轮结果(后面的计算),第二个是当前元素,得到本轮结果,最后一轮的结果就是最终结果。reduce调用reduceLeft从左往右,也可以reduceRight从右往左(实际上是递归调用,和一般意义上的从右往左有区别,看下面例子)。
    • 折叠(fold):fold(InitialVal)((CurRes, Elem) => NextRes)相对于reduce来说其实就是fold自己给初值,从第一个开始计算,reduce用第一个做初值,从第二个元素开始算。fold调用foldLeft,从右往左则用foldRight(翻转之后再foldLeft)。具体逻辑还得还源码。从右往左都有点绕和难以理解,如果要使用需要特别注意。
object HighLevelCalculations {
    def main(args: Array[String]): Unit = {
        val list = List(1, 10, 100, 3, 5, 111)
        
        // 1. map functions
        // filter
        val evenList = list.filter(_ % 2 == 0)
        println(evenList)

        // map
        println(list.map(_ * 2))
        println(list.map(x => x * x))

        // flatten
        val nestedList: List[List[Int]] = List(List(1, 2, 3), List(3, 4, 5), List(10, 100))
        val flatList = nestedList(0) ::: nestedList(1) ::: nestedList(2)
        println(flatList)

        val flatList2 = nestedList.flatten
        println(flatList2) // equals to flatList

        // map and flatten
        // example: change a string list into a word list
        val strings: List[String] = List("hello world", "hello scala", "yes no")
        val splitList: List[Array[String]] = strings.map(_.split(" ")) // divide string to words
        val flattenList = splitList.flatten
        println(flattenList)

        // merge two steps above into one
        // first map then flatten
        val flatMapList = strings.flatMap(_.split(" "))
        println(flatMapList)

        // divide elements into groups
        val groupMap = list.groupBy(_ % 2) // keys: 0 & 1
        val groupMap2 = list.groupBy(data => if (data % 2 == 0) "even" else "odd") // keys : "even" & "odd"
        println(groupMap)
        println(groupMap2)

        val worldList = List("China", "America", "Alice", "Curry", "Bob", "Japan")
        println(worldList.groupBy(_.charAt(0)))

        // 2. reduce functions
        // narrowly reduce
        println(List(1, 2, 3, 4).reduce(_ + _)) // 1+2+3+4 = 10
        println(List(1, 2, 3, 4).reduceLeft(_ - _)) // 1-2-3-4 = -8
        println(List(1, 2, 3, 4).reduceRight(_ - _)) // 1-(2-(3-4)) = -2, a little confusing

        // fold
        println(List(1, 2, 3, 4).fold(0)(_ + _)) // 0+1+2+3+4 = 10
        println(List(1, 2, 3, 4).fold(10)(_ + _)) // 10+1+2+3+4 = 20
        println(List(1, 2, 3, 4).foldRight(10)(_ - _)) // 1-(2-(3-(4-10))) = 8, a little confusing
    }
}

 参考文献:

1.notes/Scala.md at master · tch0/notes · GitHub

2.《Scala程序设计》 (中国铁路出版社)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值