天天看点

Scala入门系列(9)-Scala之集合集合集合操作

集合

Scala同时支持不可变集合和可变集合,不可变集合可以安全的并发访问。Scala默认采用不可变集合,对于几乎所有的集合类,Scala都同时提供了 可变(mutable)和不可变(immutable)的版本。Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩 展自Iterable特质,在Scala中集合有可变(mutable)和不可变 (immutable)两种类型

  • 不可变集合:scala.collection.immutable
  • 可变集合: scala.collection.mutable

不可变集合

scala不可变集合,就 是这个集合本身不能动态变化。(类 似java的数组,是不可以动态增长的)。

继承关系图:

Scala入门系列(9)-Scala之集合集合集合操作
  1. Set、Map是Java中也有的集合
  2. Seq是Java没有的,我们发现List归属 到Seq了,因此这里的List就和java不是同 一个概念了
  3. 我们前面的for循环有一个 1 to 3 , 就是IndexedSeq 下的Vector
  4. String也是属于IndexeSeq
  5. 我们发现经典的数据结构比如 Queue 和 Stack被归属到LinearSeq
  6. 大家注意Scala中的Map体系有一个 SortedMap,说明Scala的Map可以支持排
  7. IndexSeq 和 LinearSeq 的区别 [IndexSeq是通过索引来查找和定位, 因此速度快,比如String就是一个索引 集合,通过索引即可定位] [LineaSeq 是线型的,即有头尾的概念, 这种数据结构一般是通过遍历来查找, 它的价值在于应用到一些

可变集合

就是这个集合 本身可以动态变化的。(比 如:ArrayList , 是可以动态增长的)。

继承关系图:

Scala入门系列(9)-Scala之集合集合集合操作

数组

定长数组

object ArrayTest {
  def main(args: Array[String]): Unit = {
    // 第一种方式定,等同于Java中的数组,中括号的类型就是数组的类型,小括号设置数组的长度
    val array = new Array[Int](5)
    // 长度
    println(array.length)
    // 赋值,集合元素采用小括号访问
    array(0) = 2
    // 循环
    for (item <- array) {
      println(item)
    }
    // 第二种方式,在定义数组时,直接赋值
    val array002 = Array(1, 2, 3, 4, 5, 6)
    for (i <- 0 to array002.length - 1) {
      println(array002(i))
    }
  }
}
           

变长数组

ArrayBuffer是变长数组,类似java的ArrayList

object ArrayTest002 {
  def main(args: Array[String]): Unit = {
    // 定义一个数组
    val array001 = ArrayBuffer[Int]()
    // 添加一个元素 ,每append一次,arr在底层会重新分配空间,进行扩容,arr2的内存地址会发 生变化,也就成为新的ArrayBuffer
    array001.append(9090, 0, 7878)
    // 重新赋值
    array001(0) = 1010
    // 删除
    array001.remove(0)
  }
}
           

数组转换

val array = array001.toArray
    val arrayBuffer = array.toBuffer
           

多维数组

多维数组一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。矩阵与表格是我们常见的二维数组。

// 包含三个数组元素,每个数组元素又含有三个值。
    val myMatrix = Array.ofDim[Int](3, 3)
           

元组Tuple

元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。 说的简单点,就是将多个无关的数据封装为一个整体,称为元组, 最多的特点灵 活,对数据没有过多的约束。元组中最大只能有22个元素。

object TupleTest {
  def main(args: Array[String]): Unit = {
    // 创建
    val tuple = (1, 2, 2, "Hello")
    println(tuple)
    // 访问元组中的数据,可以采用顺序号(_顺序号),也可以通过索引 (productElement)访问。
    println(tuple._1)
    println(tuple.productElement(1))

    // Tuple是一个整体,遍历需要调其迭代器。
    for (item <- tuple.productIterator) {
      println(item)
    }
  }
}
           

List

Scala中的List 和Java List 不一样,在Java中List是一个接口,真正存放数据是 ArrayList,而Scala的List可以直接存放数据,就是一个object,默认情况下 Scala的List是不可变的,List属于序列Seq。

object ListTest {
  def main(args: Array[String]): Unit = {
    val list01 = List(1, 2, "555") //创建时,直接分配元素
    println(list01)
    val list02 = Nil //空集合
    println(list02)

    // 访问List元素,和数组访问方式一致
    val item = list01(0)
    println(item)

    // 添加元素:向列表中增加元素, 会返回新的列表/集合对象。
    // 注意:Scala中List元素的追加 形式非常独特,和Java不一样。

    // :+运算符表示在列表的最后增加数据,原先的集合没有变化,
    val list003 = list01 :+ 4
    // 符号::表示向集合中 新建集合添加元素。运算时,集合对象一定要放置在最右边,
    // 运算规则,从右向左。::: 运算符是将集合中的每一个元素加入到空集合中去
    val list = List(1, 2, 3)
    val listTest = 4 :: 5 :: list :: 6 :: Nil
    println("listTest:" + listTest)
    println(list003)
    // +:运算符表示在列表的最前增加数据
    val list004 = 232 +: list01
    println(list004)

  }
}
           

注意事项:

  1. List默认为不可变的集合
  2. List 在 scala包对象声明的,因此不需要引入其它包也可以使用
  3. val List = scala.collection.immutable.List
  4. List 中可以放任何数据类型,比如 arr1的类型为 List[Any]
  5. 如果希望得到一个空列表,可以使用Nil对象, 在 scala包对象声明的,因此不需 要引入其它包也可以使用

ListBuffer

ListBuffer是可变的list集合,可以添加,删除元素,ListBuffer属于序列。

object ListBufferTest {
  def main(args: Array[String]): Unit = {
    // 创建ListBuffer
    val listTest001 = ListBuffer[String]() // 空集合
    val listTest002 = ListBuffer[String]("hello", "world") //带元素的集合

    // 访问元素
    println(listTest002(0))

    // 添加元素
    listTest002 += "张三"
    listTest002.append("李四", "王五")

    // 把一个集合添加到另一个集合中
    listTest001 ++= listTest002
    println(listTest001)
    val list003 = listTest001 ++ listTest002
    println(list003)

    // 删除元素
    listTest002.remove(0)
  }
}
           

Queue

队列是一个有序列表,在底层可以用数组或是链表来实现。其输入和输出要遵循先入先出的原则。即:先存入队列的数据,要先取出。 后存入的要后取出。在Scala中,由设计者直接给我们提供队列类型使用。有scala.collection.mutable.Queue scala.collection.immutable.Queue , 一般来说,我们在开发中通常使用可变集合 中的队列。

object QueueTest {
  def main(args: Array[String]): Unit = {
    //
    import scala.collection.mutable.Queue
    // 这里的Int是泛型,希望可以存放其它类型,则使用Any即可。
    val queueTest = new Queue[Int]

    // 向队列中追加单个元素
    queueTest += 20
    println(queueTest)
    // 追加List
    queueTest ++= List(4, 5, 6)
    println(queueTest)
    //队列位,按照队列的算法,会将数据添加到队列的最后
    queueTest.enqueue(7, 8)
    println(queueTest)

    //删除元素 按照进入队列的顺序删除元素(队列先进先出)
    queueTest.dequeue() //队列头
    println(queueTest)

    // 队列 Queue-返回队列的元素
    println(queueTest.head) //返回队列的第一个元素
    println(queueTest.last) //返回队列最后一个元素
    println(queueTest.tail) //返回队列的尾部,返回除了第一个以外剩余的元素, 可以级联使用,这个在递归时使用较多。
    println(queueTest.tail.tail)
  }
}
           

Map

在Java中,HashMap 是一个散列表(数组+链表),它存储的内容是键值对(key-value)映射, Java中的HashMap是无序的,key不能重复。Scala中的Map 和Java类似,也是一个散列表,它存储的内容也是键值对 (key-value)映射,Scala中不可变的Map是有序的,可变的Map是无序的。有可变Map (scala.collection.mutable.Map) 和 不可变 Map(scala.collection.immutable.Map)

object MapTest {
  def main(args: Array[String]): Unit = {
    // 构造不可变映射
    // Scala中的不可变Map是有序,构建Map中的元素底层是Tuple2类型。
    val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> "北京")
    println(map1)
    // 构造可变映射 需要指定可变Map的包
    val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
    println(map2) // 输出顺序和声明顺序不一致
    // 创建空的映射
    val map3 = new scala.collection.mutable.HashMap[String, Int]
    println(map3)
    // 对偶元组,是只含有两个数据的元组。
    val map4 = mutable.Map(("A", 1), ("B", 2), ("C", 3), ("D", 30))
    println("map4=" + map4)

    //获取元素
    println(map1("Alice")) // 使用map(key),如果key不存在,则抛出异常[java.util.NoSuchElementException],在Java中,如果key不存在则返回null
    // 判断是否存在某个KEY
    println(map1.contains("aaa"))
    // 使用map.get(key).get取值,key存在返回some,如果key不存在,则返回None
    println(map1.get("Alice"))
    println(map1.get("aaa"))
    println(map1.get("Alice").get)
    // 使用map4.getOrElse()取值,如果key存在,返回key对应的值。不存在,返回默认值
    println(map1.getOrElse("aaa", "默认值"))

    // 添加元素
    map2 += ("lisan" -> 10)
    map2 += ("lisan" -> 20, "wangwu" -> 50)
    println(map2)

    // 删除元素
    map2 -= ("lisan", "aaa")
    println(map2)

    // 遍历
    for ((k, v) <- map2) {
      println(k + ":" + v)
    }
    for (k <- map2.keys) {
      println(k + ":" + map2.get(k).get)
    }
  }
}
           

Set

集是不重复元素的结合。集不保留顺序,默认是以哈希集实现。

java中,HashSet是实现Set接口的一个实体类,数据是以哈希表的形式存放 的,里面的不能包含重复数据。Set接口是一种不包含重复元素的 collection, HashSet中的数据也是没有顺序的。

默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。

object SetTest {
  def main(args: Array[String]): Unit = {
    // 不可变集合
    val setTest = Set(1, 2, 3)
    println(setTest)

    // 可变集合
    import scala.collection.mutable.Set
    val mutableSet = Set(1, 2, 3)
    println(mutableSet)

    // 元素添加,如果添加的对象已经存在,则不会重复添加,也不会报错
    mutableSet.add(4)
    mutableSet += 5
    mutableSet.+=(7, 8, 9)
    println(mutableSet)

    // 元素删除,如果删除的对象不存在,则不生效,也不会报错
    mutableSet.remove(1)
    mutableSet -= 2
    println(mutableSet)

    // 遍历
    for (v <- mutableSet) {
      println(v)
    }
  }
}

           

集合操作

map映射

将集合中的每一个元素通过指定功能 (函数)映射(转换)成新的结果集合这里其实就是所谓的将函数作为参数传递给另外一个函数,这是函数式编程的特点。

object CollectionTest {
  def main(args: Array[String]): Unit = {
    // 将 val names = List("Alice", "Bob", "Nick") 中的所有单词,全部转成字母大写, 返回到新的List集合中
    def upper(name: String): String = {
      name.toUpperCase
    }

    val names = List("Alice", "Bob", "Nick")
    val strings: List[String] = names.map(upper)
    println(strings)
  }
}
           

flatmap映射

flat即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映 射到某个函数并返回新的集合。

val value: Any = names.flatMap(upper)
    println(value)
           

filter

将符合要求的数据(筛选)放置到新的集合中。

// 将 val names = List("Alice", "Bob", "Nick") 集合中首字母为'A'的 筛选到新的集合。
    def startWithA(name: String): Boolean = {
      name.startsWith("A")
    }

    val strings1: List[String] = names.filter(startWithA)
    println(strings1)
           

化简

将二元函数引用于集合中的函数。

//val list = List(1, 20, 30, 4 ,5) , 求出list的和
    val list = List(1, 20, 30, 4, 5)
    def sum(n1: Int, n2: Int): Int = {
      n1 + n2
    }
    val res = list.reduceLeft(sum)
    println("res=" + res)
           

折叠

fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list 中的所有元素被遍历。

val list = List(1, 2, 3, 4)
    def minus(num1: Int, num2: Int): Int = {
      num1 - num2
    }
    println(list.foldLeft(5)(minus)) // 函数的柯里化
    println(list.foldRight(5)(minus)) 
           

扫描

即对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置 于一个集合中保存。

在这里插入代码片
           

拉链(合并)

当我们需要将两个集合进行 对偶元组合并,可以使用拉链。

val list1 = List(1, 2, 3)
    val list2 = List(4, 5, 6)
    val list3 = list1.zip(list2)
    println(list3)
           

迭代器

通过iterator方法从集合获得一个迭代器,通过while循环和for表达式对集合进行 遍历.(学习使用迭代器来遍历)。

val list1 = List(1, 2, 3)
    val iterator: Iterator[Int] = list1.iterator
    while (iterator.hasNext) {
      println(iterator.next())
    }
           

流 Stream

stream是一个集合。这个集合,可以用于存放无穷多个元素,但是这无穷个元 素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产,末尾元 素遵循lazy规则(即:要使用结果才进行计算的) 。

object StreamTest {
  def main(args: Array[String]): Unit = {
    // 格式:numsForm(参数:参数类型) : 返回值类型 = 参数 #:: numsForm(m+1)
    def numsForm(n: BigInt): Stream[BigInt] = n #:: numsForm(n + 1)
    val stream1 = numsForm(1)
  }
}
           

注意事项:

  1. Stream 集合存放的数据类型是BigInt
  2. numsForm 是自定义的一个函数,函数名是程序员指定的。
  3. 创建的集合的第一个元素是 n , 后续元素生成的规则是 n + 1
  4. 后续元素生成的规则是可以程序员指定的 ,比如 numsForm( n * 4)…

视图 View

Stream的懒加载特性,也可以对其他集合应用view方法来得到类似的效果。

注意事项:

  1. view方法产出一个总是被懒执行的集合。
  2. view不会缓存数据,每次都要重新计算, 比如遍历View时。

线程安全的集合

所有线程安全的集合都是以Synchronized开头的集合。

并行集合

Scala为了充分使用多核CPU。主要用到的算法有: Divide and conquer : 分治算法,Scala通过splitters(分解器),combiners (组合器)等抽象层来实现,主要原理是将计算工作分解很多任务,分发给 一些处理器去完成,并将它们处理结果合并返回。

操作符

  1. 如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号反引号
  2. 置操作符:A 操作符 B 等同于 A.操作符(B)
  3. 后置操作符:A操作符 等同于 A.操作符,如果操作符定义的时候不带()则调用 时不能加括号
  4. 前置操作符,+、-、!、~等操作符A等同于A.unary_操作符
  5. 赋值操作符,A 操作符= B 等同于 A = A 操作符 B ,比如 A += B 等价 A = A + B

继续阅读