Scala(八)-①-数据结构-集合操作-线程安全的集合-操作符重载

① 集合操作

Why

为什么需要集合操作?集合操作都包括哪些?Scala的集合操作主要为了适应大数据的发展,我们以Map为例.于事需入局,于程需入题,先看下题.

入题

请将list(3,5,7) 中的所有元素都 * 2,将其结果放到一个新的集合中返回,即返回一个新的list(6,10,14), 请编写程序实现.

传统解决方案
object InQuestion {
  def main(args: Array[String]): Unit = {
    val list1 = List(3, 5, 7)
    var list2 = List[Int]()
    for (item <- list1) {
      list2 = list2 :+ item * 2
    }
    println(list2)
  }
}

优点:

  • 处理直接

缺点:

  • 不够简洁
  • 没有提现出函数式编程
  • 不利于复杂的数据处理业务
map映射操作解决方案
分析
  • 其实就是一个关于集合元素映射操作的问题
  • 在Scala中可以通过map映射操作来解决:将集合中的每一个元素通过指定功能(函数)映射(转换)成新的结果集合.这里其实就是所谓的将函数作为参数传递给另外一个函数,这是函数式编程的特点.
HashMap的API为例

def map[B](f: (A) ⇒ B): HashSet[B]

  • [B]泛型
  • map是一个高阶函数,可以接收函数f:(A)=>B
  • HashSet[B]就是返回的新的集合
代码

将函数作为变量进行传递,其会在map函数内部再对f1进行调用,一般都会对每一个元素调用一次.

  def testCaseForMap(): Unit = {

    println("=============map=============")
    val list1 = List(3, 5, 7)
    def f1(n1: Int): Int = {
      2 * n1
    }
    val list2 = list1.map(f1)
    println(list2)
  }

How

相关的函数

语法和规则

扁平化映射-flatmap函数

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

object CollectionOpDemo01ForFlatMap {
  def main(args: Array[String]): Unit = {
    val names = List("Alice", "Bob", "Nick")
    def upper( s : String ) : String = {
      s. toUpperCase
    }

    println(names.flatMap(upper))
  }
}

过滤-filter函数

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

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

    val names = List("Alice", "Bob", "Nick")
    def startA(s:String): Boolean = {
      s.startsWith("A")
    }
    val names2 = names.filter(startA)
    println("names=" + names2)
  }
}
化简(聚合)-reducerLeft

对集合依据函数进行聚合运算,reducerLeft中的函数式二元函数.

object CollectionOpDemo03ForReducerLeft {
  def main(args: Array[String]): Unit = {
    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)

  }
}

练习

  • 使用化简的方法求出 List(3,4,2,7,5) 最小的值
  def testCaseForWork01(): Unit = {
    val list = List(1, 2, 3, 4 ,5)
    def minus( num1 : Int, num2 : Int ): Int = {
      num1 - num2
    }
    println(list.reduceLeft(minus)) // 输出?  -13 // ((((1-2)-3)-4)-5) = 1-(2+3+4+5)
    println(list.reduceRight(minus)) //输出?  3   // (1-(2-(3-(4-5)))) =
  }


  def testCaseForWork02(): Unit = {
    var l = List(3,4,2,7,5)

    def max(num1: Int,num2: Int):Int = {
      if (num1 > num2) num1 else num2
    }
    def min(num1: Int,num2: Int):Int = {
      if (num1 < num2) num1 else num2
    }
    println(l.reduceLeft(max))
    println(l.reduceLeft(min))

  }
折叠-fold

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

  • 可以把reduceLeft看做简化版的foldLeft.
  override /*TraversableLike*/
  def reduceLeft[B >: A](f: (B, A) => B): B =
    if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft")
    else tail.foldLeft[B](head)(f)

代码

object CollectionOpDemo04ForFold {


  def main(args: Array[String]): Unit = {
    // 折叠
    val list = List(1, 2, 3, 4)
    def minus( num1 : Int, num2 : Int ): Int = {
      num1 - num2
    }
    println(list.foldLeft(5)(minus))  // 相当于list(5, 1, 2, 3, 4)  list.reduceLeft(minus)
    println(list.foldRight(5)(minus)) // 相当于list(1, 2, 3, 4, 5)  list.reduceLeft(minus)

  }

}
  • foldLeft和foldRight 缩写方法分别是:/:和:\
  def testCaseForWork01(): Unit = {
    println("testCaseForWork01")
    val list4 = List(1, 9, 2, 8)
    def minus(num1: Int, num2: Int): Int = {
      num1 - num2
    }
    var i6 = (1 /: list4) (minus) // ==> list4.foldLeft(0)(minus)    1-20
    println(i6)
    i6 = (100 /: list4) (minus)  // 100 - 20
    println(i6)
    i6 = (list4 :\ 10) (minus) // list4.foldRight(10)(minus) // -4
    println(i6) 

  }
扫描-scanLeft-scanRight

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

object CollectionOpDemo05ForScan {
  def main(args: Array[String]): Unit = {
    def minus( num1 : Int, num2 : Int ) : Int = {
      num1 - num2
    }
    // scanLeft 计算每次折叠结果,第一次直接保存
    //5 (1,2,3,4,5) =>(5,4,2,-1,-5,-10)
    val i8 = (1 to 5).scanLeft(5)(minus) //IndexedSeq[Int]
    println(i8)
    def add( num1 : Int, num2 : Int ) : Int = {
      num1 + num2
    }
    //5 (1,2,3,4,5) =>(5,6,8, 11,15,20)
    // (1,2,3,4,5),5  =>  (20,19,17,14,10,5)
    val i9 = (1 to 5).scanRight(5)(add) //IndexedSeq[Int]
    println(i9)

  }
}
拉链-zip

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

image.png
val list1 = List(1, 2 ,3)
val list2 = List(4, 5, 6)
val list3 = list1.zip(list2) 
println("list3=" + list3)
  • 拉链的本质就是两个集合的合并操作,合并后每个元素是一个 对偶元组
  • 集合不限于List, 也可以是其它集合比如 Array
迭代器

所有的集合都有迭代器,可以用迭代器来进行遍历.

  • 使用迭代器遍历
object CollectionOpDemo06ForIterator{


  def main(args: Array[String]): Unit = {
    val iterator = List(1, 2, 3, 4, 5).iterator //
    println("--------遍历方式1 -----------------")
    while (iterator.hasNext) {
      println(iterator.next())
    }
    println("--------遍历方式2 for -----------------")
    for(enum <- iterator) {
      println(enum) //
    }

  }
}

原理

  • iterator的构建实际是AbstractIterator的一个匿名子类,该子类提供了hasNext next等方法
  override /*IterableLike*/
  def iterator: Iterator[A] = new AbstractIterator[A] {
    var these = self
    def hasNext: Boolean = !these.isEmpty
    def next(): A =
      if (hasNext) {
        val result = these.head; these = these.tail; result
      } else Iterator.empty.next()
流-stream

一句话:流是一个集合,用于存放无穷多个元素.末尾元素遵守lazy规则,即要使用才会进行计算.

  • 使用tail会自动计算生成新的数据
  • 使用head获取头元素
  • 不能使用last,会导致程序无限循环
object CollectionOpDemo07ForStream {


  def testCaseForStream01(): Unit = {
    //创建Stream
    def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
    def multi(x:BigInt) : BigInt = {
      x * x
    }
    println(numsForm(5).map(multi)) //?
  }

  def testCaseForQuicklyStart() = {

    //创建Stream
    def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
    val stream1 = numsForm(1)
    println(stream1) //
    //取出第一个元素
    println("head=" + stream1.head) //
    println(stream1.tail) //
    println(stream1) //?
  }

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

    testCaseForQuicklyStart()
    testCaseForStream01()
  }
}
视图-view
  • 对其他集合应用view方法来得到类似Stream的懒加载特性效果.这在python中叫做generator.
  • view主要用在当要生成大量数据的时候,如果一次性生成会占用大量内存.
object CollectionOpDemo08ForView {

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

    def eq(i: Int): Boolean = {
      i.toString.equals(i.toString.reverse)
    }
    //1.没有使用view
    val viewSquares1 = (1 to 100).filter(eq)
    println(viewSquares1)

    // 2.使用view来迭代生成大量数据
    val viewSquares2 = (1 to 100)
      .view
      .filter(eq)

    for (item <- viewSquares2) {
      println(item)
    }

    println(viewSquares2)
  }
}

② 线程安全的集合

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

SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
SynchronizedStack

Why

  • Scala提供了针对可变集合的线程安全集合.
  • Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算

How

parallel并行DEMO

打印1~5

代码

    val maxNumber = 1000000

    // 单核心,输出有序
    var startTime = System.currentTimeMillis()
    (1 to maxNumber).foreach(println(_))
    var endTime = System.currentTimeMillis()
    val t1 = (endTime-startTime)/1000
    println(s"单核计算花费了${t1}秒")

    println()
    // 多核计算,输出无序
    startTime = System.currentTimeMillis()
    (1 to maxNumber).par.foreach(println(_))
    endTime = System.currentTimeMillis()
    val t2 = (endTime-startTime)/100
    println(s"单核计算花费了${t2}秒,快了${t2/t1}倍")

输出

查看并行集合中元素访问的线程
val result1 = (0 to 100).map{case _ => Thread.currentThread.getName}
val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}
println(result1)
println(result2)

What

  • Divide and conquer分治算法

Scala通过splitters,combiners等抽象层来实现,主要原理是将计算工作分解很多任务,分发给一些处理器去完成,并将它们处理结果合并返回

  • Work stealin算法

主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,å这样达到尽早干完的目的

③ 操作符重载

Scala中重载了大量操作符,并且准许程序员自定义重载操作符.

Why

方便使用操作符对进行计算

How

  • 一元操作符-后置操作符: AOp 等同于 A.操作符, 如果操作符定义的时候不带()则调用时不能加括号
  • 一元操作符-前置操作符: +、-、!、~等操作符A等同于A.unary_操作符
  • 二元操作符~中置操作符: A Op B 等同于 A.op(B)
  • 二元操作符-赋值操作符:A op= B 等同于 A = A op B. 比如 A += B 等价 A = A + B
object CollectionOpDemo10ForOpOverload{

  def main(args: Array[String]): Unit = {
      val money = new Money(100)
      money + 1
      money += 1
      money++;
      !money
      println(money.money)
  }
}

class Money(n : Int) {

  var money : Int = n

  // 二元-中置操作符
  def +(n: Int):Money = {
      this.money += n
      this
  }

  // 二元赋值操作符
  def +=(n: Int):Money = {
    this.money += n
    this
  }

  // 一元操作符 ++
  def ++():Money = {
    this.money += 1
    this
  }

  // 一元操作符 !
  def unary_!():Money = {
    this.money = -this.money
    this
  }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容

  • Scala与Java的关系 Scala与Java的关系是非常紧密的!! 因为Scala是基于Java虚拟机,也就是...
    灯火gg阅读 3,440评论 1 24
  • 转载自:https://halfrost.com/go_map_chapter_one/ https://half...
    HuJay阅读 6,143评论 1 5
  • Scala的集合类可以从三个维度进行切分: 可变与不可变集合(Immutable and mutable coll...
    时待吾阅读 5,815评论 0 4
  • 这是倒霉的一天,先是华盛顿大桥车祸,堵得水泄不通。等我好不容易上了地铁,发现地铁也出了问题,我已经比平时上班晚了一...
    纽约蓝蓝阅读 973评论 1 8
  • 慢慢的,发现,要有一双发现美的眼睛,这个世界上,有太多的美好。为什么不去多注意呢?每个人都有自己的开心和不开心。只...
    一个神经质的疯子阅读 134评论 0 0