Scala 集合与算子

Scala 同时支持不可变集合和可变集合,不可变集合可以安全的并发访问,Scala 默认采用不可变集合。
可变集合: scala.collection.mutable,可以对集合中的元素进行增删改, 返回的是原集合
不可变集合:scala.collection.immutable,也可以对集合中的元素进行增删改, 但不会修改原集合, 会返回一个新的集合。

一、数组(Array)

数组并不属于集合中数据结构,但是数组的使用比较频繁。
在 JVM 中, Scala 的Array就是以 Java 数组的形式出现的。
定长数组

object ArrayDemo1 {
  def main(args: Array[String]): Unit = {
    // 创建一个定长数组
    val arr1 = new Array[Int](10) //方括号代表泛型,圆括号内的数代表数组长度,所有元素初始化为 0
    val arr2 = new Array[String](5) //所有元素初始化为 null
    println(arr1(0))  // 使用(index)的方式来访问数组中的元素
    println(arr2(1))
  }
}

变长数组
在 Scala 中变长数组为 <font color="fbbc05">ArrayBuffer</font>,类似于 Java 中的 ArrayList

object ArrayBufferDemo1 {
  def main(args: Array[String]): Unit = {
    // 创建一个空的变长数组
    val arr1 = new ArrayBuffer[Int] 
    arr1 += 1   // 1.用 += 的方式添加元素   
    arr1 += (2, 3, 4, 2)    // 可以同时添加多个元素
    println(arr1) // ArrayBuffer(1, 2, 3, 4, 2)
   
    arr1 -= 2    // 2.用 -= 的方式移除碰到的第一个2
    println(arr1)   // ArrayBuffer(1, 3, 4, 2)
    
    arr1 ++= Array(100, 200, 300)   // 3.使用 ++= 可以追加任何的集合
    println(arr1) // ArrayBuffer(1, 3, 4, 2, 100, 200, 300)
   
    arr1.insert(2, -1, -2)   // 4.从下标为2的位置插入 -1, -2
    println(arr1) // ArrayBuffer(1, 3, -1, -2, 4, 2, 100, 200, 300)
    
    arr1.remove(2)  // 5.删除下标为2的元素
    println(arr1) // ArrayBuffer(1, 3, -2, 4, 2, 100, 200, 300)
    
    arr1.remove(2, 2) // 6.从下标为2开始,删除2个元素
    println(arr1) // ArrayBuffer(1, 3, 2, 100, 200, 300)
  }
}

数组的常用算子

object ArrayBufferDemo2 {
  def main(args: Array[String]): Unit = {
    var arr = Array(10, 21, 32, 4, 15, 46, 17)

    println(arr.sum) // 求和
    println(arr.max)
    println(arr.min)

    println(arr.head) // 1.返回数组的第一个元素
    println(arr.tail) // 2.返回出第一个以外的其他元素集合

    println(arr.last) // 返回数组的最后一个元素
    println(arr.take(2)) // 3.返回数组前2个元素组成的集合

    println(arr.mkString) // 把数组变成字符串
    println(arr.mkString(",")) // 4.把数组变成字符串, 元素之间用,隔开

    // 5.排序, 并返回一个排好序的数组 : 默认是升序
    val arrSorted: Array[Int] = arr.sorted
    println(arrSorted.mkString(","))
    // 6.降序
    var arrSorted1 = arr.sortWith(_ > _)
    println(arrSorted1.mkString(","))
  }
}

多维数组

object ArrayDemo5 {
  def main(args: Array[String]): Unit = {
    // 创建一个 2 * 3 的二维数组
    val matrix: Array[Array[Int]] = Array.ofDim[Int](2, 3)
    // 使用两对圆括号给数组中的元素赋值
    matrix(0)(0) = 100
    val i: Int = matrix(0)(0)
    println(i)

    // 创建不规则的数组
    var matrix2 = new Array[Array[Int]](3)
    for (i <- 0 until matrix2.length) {
      matrix2(i) = new Array[Int](i + 1)
    }
  }
}

可变数组与不可变数组共有的操作
++ 连接两个数组
++: 连接两个数组
:+ 一个数组连接一个元素
+: 一个数组连接一个元素
/: 左折叠
:\ 右折叠
head 第一个元素(重要)
tail 除第一个元素为其他元素组成的数组(重要)
last 最后一个元素
max 找到最大值
min 找到最小值
可变数组独有的操作
++= 添加数组的元素到原来的数组中
++=:
+= 添加元素到数组中
+=:
- 返回新的去掉指定元素的数组
-- 返回新的元素
-= 修改原数组. 去掉第一次指定出现的元素
--=

二、元组(Tuple)

元组为一个容器,可以存放各种相同或不同类型的数据。元组最多存储 22 个数据。

object TupleDemo {
  def main(args: Array[String]): Unit = {
    // 1.tuple 的两种声明方式,Tuple22 为最大的tuple,最多存储22个元素
    val t1 = Tuple2(122, "李四")
    val t2 = (1, "小明", true)
    // 2.访问元组中的元素
    println(t1._2)
    println(t2._2)
    // 3.使用迭代器,遍历元组
    for (elem <- t1.productIterator) {
      println(elem)
    }
  }
}

三、列表(List)

Scala 的 List 和 Java 的 List 不一样,在 Java 中 List是一个接口,真正存放数据是ArrayList,而 Scala 的List可以直接存放数据,就是一个object,默认情况下 Scala 的 List 不可变的。 List 在 Predef 表下默认导入,不用 new。

object ListDemo {
  def main(args: Array[String]): Unit = {
    // 1.创建List 方式一
    val list1 = List(10, 20, 30, 40 )
    val list2 = 10::20::30::40::Nil // 方式二,使用::创建list,::为List的专用符号

    list1(2) // 2.访问list1下标为2的元素

    val list3 = list1 :+ 50 :+ 60    // 3.使用 :+ 向列表尾部追加元素
    val list4 = 1 +: 2 +: list2     // 4.使用 +: 向列表头部追加元素

    // 5.使用:::, ++可以连接两个列表 (concat()方法也可以)
    val list5 = list1 ::: list2
    val list6 = list1 ++ list2
    val list7 = List.concat(list1, list2)
  }
}

ListBuffer 可变列表,操作和 List 类似

四、队列(Queue)

队列是一个有序列表,在底层可以用数组或是链表来实现。队列遵循 先进先出 原则,进入队列 enqueue(),离开队列 dequeue()

object QueueDemo {
  def main(args: Array[String]): Unit = {
    // 1. 创建空队列
    val queue1 = new mutable.Queue[Int]
    // 2. 创建队列, 并给队列初始化两个值
    var queue2 = mutable.Queue(10, 20)
    // 向对类添加一个元素
    queue1 += 10
    queue1 += 20
    println(queue1)   // Queue(10, 20)
    // 把 List 中元素添加到队列中
    queue1 ++= List(1,2,3)
    println(queue1)   // Queue(10, 20, 1, 2, 3)
    // 把数组中的元素添加到队列中
    queue1 ++= Array(5,6,7)
    // 使用方法添加元素
    queue1.enqueue(100, 200)
    println(queue1)   // Queue(10, 20, 1, 2, 3, 5, 6, 7, 100, 200)
    // 删除队列的第一个元素
    queue1.dequeue()
    println(queue1)   // Queue(20, 1, 2, 3, 5, 6, 7, 100, 200)
    // 返回队列头部元素
    println("queue1.head = " + queue1.head)   // queue1.head = 20
    // 返回队尾元素: 返回的是除了队头后的所有元素组成的队列
    println("queue1.tail = " + queue1.tail)   // queue1.tail = Queue(1, 2, 3, 5, 6, 7, 100, 200)
  }
}

五、映射 (Map)

Scala 中不可变的 Map 的大小在小于等于4个时是有序的,可变的 Map 是无序的。
映射中的键值对是以元组的形式存在的,Map 在 Predef 表下默认导入,不用 new。
创建 Map

object MapDemo1 {
  def main(args: Array[String]): Unit = {
    // 1.创建不可变映射 方式1 推荐, 可读性好
    val map1 = Map("C" -> 100, "A" -> 50, "B" -> "20")
    // 方法2: 不如方法1可读性好
    val map2 = Map(("C", 100), ("A", 50), ("B", "20"))
    
    // 2.遍历出来的每个元素都是一个元组
    for (elem <- map1) {
      println(elem)  
      println("key = " + elem._1 + "    " + "value = " + elem._2)
    }
      
    // 创建可变映射
    val map3 = mutable.Map("C" -> 100, "A" -> 50, "B" -> "20", "D" -> "35")
    for (elem <- map3) {
      println(elem)   // 遍历可知,可变映射是无序的
    }   
  }
}

Map 取值

object MapDemo2 {
  def main(args: Array[String]): Unit = {
    val map = mutable.Map("C" -> 100, "A" -> 50, "B" -> "20", "D" -> "35")
    println(map("C"))    // 1.map(key) 获取元素,若没有直接报错
    println(map.get("E"))  // 2.map.get(key)  获取元素,若没有返回 None
    val kv2 = map.getOrElse("E", 39) // 3.获取 "E" 若没有,将 "E" -> 39 存入 map1,并返回 "E" -> 39
    println(kv2)
  }
}

Map 增删该查
只能对可变映射进行增删改,通过不可变映射获取到新的映射

object MapDemo3 {
  def main(args: Array[String]): Unit = {

    val map1 =
      mutable.Map("a" -> 1, "c" -> 2, "b" -> 3)
    // 修改值
    map1("a") = 100
    // 添加键值对: key不存在的时候就是添加
    map1("aa") = 120
    println(map1)

    // 添加多个键值对
    map1 += ("bb" -> 11, "cc" -> 12)
    println(map1)

    // 删除映射关系,删除 k 即删除kv
    map1 -= "a"
    println(map1)

    // 连接两个映射
    val map2 = map1 ++ Map("aaa"-> 4, "bbb" -> 5)
    println("map2 = " + map2)
    println("map1 = " + map1)
  }
}

六、集合(Set)

Scala 的 Set 和 Java 的 Set 很类似,都是不重复的元素的集合,且不保留顺序, 默认以 HashSet 的形式实现。

object SetDemo {
  def main(args: Array[String]): Unit = {
    //创建一个不可变 Set
    val set1 = Set(10, 2, 5, 9, "a", "bb", "aa")
    println(set1) // 集合无序
    val set3 = Set(0, 12, 15, 9, "a", "cc", "aa")
    //并集
    println(set1 ++ set3)
    println(set1.union(set3))
    println(set1 | set3)
    //交集
    println(set1 & set3)
    println(set1.intersect(set3))
    //差集
    println(set1 &~ set3)
    println(set1 -- set3)
    println(set1.diff(set3))

    import scala.collection.mutable
    //创建一个可变 Set
    val set2 = mutable.Set("a", "c", "b")
    println(set2)
    // 向Set集合中添加元素. set1会被更改
    set2 += "abc"
    println(set2)
    // 删除 Set 集合中的元素
    set2 -="a"
    println(set2)
    // 删除不存在的元素也不会抛出异常
    set2 -="cccc"
  }
}

七、Scala 算子

7.1 map

用来调整数据结构,一个集合 map 之后,它的长度不会变。

// 请将 List(1,2,3,4) 中的所有元素都 * 2 ,将其结果放到一个新的集合中返回,即返回一个新的 list(6,10,14)
object mapDemo {
  def main(args: Array[String]): Unit = {
    val list1 = List(1, 2, 3, 4);
    val list2 = list1.map(x => x * x)
    println(list2)    // List(1, 4, 9, 16)
  }
}

7.2 flatMap

会将每一个元素映射为一个集合,然后将每个集合扁平化到一个大的集合中, 一般情况下会增加集合的长度。传进去的函数的返回值必须是一个集合。

object flatMapDemo {
  def main(args: Array[String]): Unit = {
    val list1 = List("hello world", "alibaba hello", "hello hello hello")

    val list2 = list1.flatMap( x=> x.split(" "))
    println(list2)

    val list3 = List(30, 50, 70, 60, 10, 20)
    val list4 = list3.flatMap(x => Array(x, x * x, x * x * x))  // (20,900, 27000,...)
    println(list4)
  }
}

7.3 filter

过滤的意思就是把经过函数处理返回true的元素放在新的集合中

object filterDemo {
  def main(args: Array[String]): Unit = {
    // 过滤是所有为Int的元素,并给这些值加一
    val list1 = List(null, 30, 50, 70, 60, 10, 20, true, "a")
    val list2 = list1
      .filter(_.isInstanceOf[Int])
      .map(_.asInstanceOf[Int])
      .map(_ + 1)
    println(list2)
  }
}

7.4 reduce

reduce 的逻辑: 从1,2开始操作,得到临时结果 n,接着用 n 和 3 进行操作得到得到临时结果 n1,接着用 n1...... 直到没有下一个元素为止

object ReduceDemo {
  def main(args: Array[String]): Unit = {
    // 求集合中的所有元素的和
    val list1 = List(30, 50, 70, 60, 10, 20)
    // val result = list1.reduce((x, y) => x + y)
    val result = list1.reduce(_ + _)
    println(result)

    // 将集合中的元素用 - 连起来
    val ss = List("a", "b", "c")
    //        println(ss.reduce((x, y) => x + "-" + y))
    println(ss.reduce(_ + "-" + _))
  }
}

7.5 fold

reduce 是从前两个元素开始归纳的,而 fold 允许提供一个初始化值, 这个初始值是传递进去的函数的第一次执行时候的第一个参数, 后面的执行就和 reduce 一样了。

object foldDemo {
  def main(args: Array[String]): Unit = {
    val list1 = List(30, 50, 70, 60, 10, 20)
      // 1000 依次减去 list 中的元素得到的结果
    val result = list1.fold(1000)(_ - _)
    println(result)
  }
}

初始值 : \ 为右折叠(foltRight),/ : 初始值 为左折叠(foltLeft)理解运算符: 的结合性。

7.6 scan

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

object scanDemo {
    def main(args: Array[String]): Unit = {
      // 1000 减去集合中的每一个元素,同时记录每一计算的结果到一个集合中
      var list1 = List(10, 1, 3, 5, 8, 9, 22, 89)
      // 从左往右减 scan = scanLeft
      val list2 = list1.scan(1000)(_ - _)
      println(list2)  
      // 从右往左减
      val list3 = list1.scanRight(1000)(_ - _)
      println(list3)
    }
}

7.7 zip

将两个集合进行合并成元组组成的集合,可以使用zip;将一个元素为元组的集合分成两个集合,可以使用unzip。zipWithIndex 将元素和下标做zip操作,返回下标加元素的数组。

object ZipDemo {
  def main(args: Array[String]): Unit = {
    val list1 = List(30, 50, 70, 60, 10, 20, 100, 200)
    val list2 = List(3, 5, 7, 6, 1, 2)

    // 得到的集合长度, 以少的为准
    val list3: List[(Int, Int)] = list1.zip(list2)
    println(list3)
    // 得到的集合长度,以多的为准
    val list4 =  list1.zipAll(list2, -1, -2)
    println(list4)
    // 元素和下标进行拉链
    val list5: List[(Int, Int)] = list1.zipWithIndex
    val list6 = list3.filter(_._2 % 2 == 1).map(_._1)
    println(list6)
  }
}

7.8 groupBy

groupBy 将集合中的每一个元素进行处理,得到的结果相同的放入同一个集合中

object groupbyDemo {
  def main(args: Array[String]): Unit = {
    val list1 = ListBuffer(30, 50, 7, 6, 1, 20)
    // 将奇偶数分开
    val map = list1.groupBy(x => x % 2 == 1)
    println(map)
  }
}
---------------输出
Map(false -> ListBuffer(30, 50, 6, 20), true -> ListBuffer(7, 1))

7.9 sort

排序相关的算子有三个 sorted、sortedBy、sortWith
排序的本质是比较元素的大小
方式一:需要让元素本身具有比较能力。让元素类继承 Ordered 类,覆写 compare 方法

object SortDemo1 {
    def main(args: Array[String]): Unit = {
        println(new User(20, "lisi") > new User(10, "zs"))  // false
        val users = List(new User(20, "lisi"), new User(10, "zs"), new User(15, "wangwu"), new User(15, "abc"))
        
        println(users.sorted)
    }
}
class User(val age: Int, val name: String) extends Ordered[User] {
   
    override def toString: String = s"[$name, $age]"
    override def compare(o: User): Int = {
        var r = o.age - this.age
        if (r == 0) {
            r = this.name.compareTo(o.name)
        }
        r
    }
}
----------------输出
false
List([lisi, 20], [abc, 15], [wangwu, 15], [zs, 10])

方式二:需要提供第三方比较器

  1. 使用 sorted 算子,传入匿名内部类对象 new Ordering,覆写 compare 方法
object SortDemo2 {
  def main(args: Array[String]): Unit = {
    val users = List(new User1(20, "lisi"), new User1(10, "zs"), new User1(15, "wangwu"), new User1(15, "abc"))
    // 使用 sorted 算子,传入匿名内部类对象 new Ordering,覆写 compare 方法
    val users1 = users.sorted(new Ordering[User1] {
      override def compare(x: User1, y: User1): Int = x.age - y.age
    }.reverse)
    println(users1)
  }
}

class User1(val age: Int, val name: String){
  override def toString: String = s"[$name, $age]"
}
  1. 使用 sortBy 算子,第一个参数列表传入函数式,第二个参数列表默认传入 Ordering.Int 。推荐使用。
object SortDemo3 {
  def main(args: Array[String]): Unit = {
    val users = List(new User2(20, "lisi"), new User2(10, "zs"), new User2(15, "wangwu"), new User2(15, "abc"))
    // 1. 将user拆解成元组(user.age, user.name),传入Ordering.Tuple2,
    //   Ordering.Int.reverse按年龄倒序,Ordering.String.reverse按姓名正序排列
    val res = users.sortBy(user => (user.age, user.name))(Ordering.Tuple2(Ordering.Int.reverse, Ordering.String.reverse))
    println(res)
      
    // 2. 默认传入Ordring.Int, 按元素大小排列
    val list1 = List(30, 50 ,70, 60, 10, 20)
    println(list1.sortBy(x => x))
    val strs = List("abc", "abcd", "maben", "shenzhen")
    // 按字符串长度倒序排列
    println(strs.sortBy(x => x.length)(Ordering.Int.reverse))
  }
}

class User2(val age: Int, val name: String){
  override def toString: String = s"[$name, $age]"
}

3.使用 sortWith 算子,sortWith 底层是调用 sorted 传入 Ordering 对象,实际需要传入两个比较的元素,提供判断逻辑,返回Boolean

object SortDemo4 {
    def main(args: Array[String]): Unit = {
        val list1 = List(30, 50, 70, 60, 10, 20)
        println(list1.sortWith((x, y) => x < y))    // 如果 x<y 就正序排列,倒序反之
    }
}
-------------输出
List(70, 60, 50, 30, 20, 10)

关于 Sort 的总结: 推荐使用 sortBy[B](f: A => B)(implicit ord: Ordering[B]) 这种方法,不用涉及具体的判断逻辑,只需提供函数式和 Ordering 的隐式比较函数。

八、部分应用函数举例

object partfunctionDemo {
  def main(args: Array[String]): Unit = {

    def add(x: Int, y: Int): Int = {
      x + y
    }
    // 根据 add 函数,返回一个部分应用函数,这个函数值专门用来计算某个数与 2 相加的结果
    val add2:Int => Int = add(_, 2)
    println(add2(5))
    println(add2(3))
    println(add2(6))
  }
}

九、Scala 读取文件

使用 Source.fomFile(path) 获取文件

import scala.io.Source
object FileDemo1 {
    def main(args: Array[String]): Unit = {
        val path = "文件路径"
        // getLines 将得到一个迭代器,其中每一行为一个元素,toList 后更方便操作
        val lines: List[String] = Source.fromFile(path).getLines().toList
        lines.foreach(x => println(x))
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272