3.大数据技术之Scala_五章:数据结构

五  数据结构

5.1  数据结构特点

Scala同时支持可变集合和不可变集合,不可变集合从不可变,可以安全的并发访问。

两个主要的包:

不可变集合:scala.collection.immutable

可变集合:  scala.collection.mutable

Scala优先采用不可变集合,对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本。

不可变集合继承层次:

 

可变集合继承层次:

 

5.2  数组 Array

1) 定长数组

//定义

val arr1 = new Array[Int](10)

//赋值

arr1(1) = 7

或:

//定义

val arr1 = Array(1, 2)

2) 变长数组

//定义

val arr2 = ArrayBuffer[Int]()

//追加值

arr2.append(7)

//重新赋值

arr2(0) = 7

3) 定长数组与变长数组的转换

arr1.toBuffer

arr2.toArray

4) 多维数组

//定义

val arr3 = Array.ofDim[Double](3,4)

//赋值

arr3(1)(1) = 11.11

5) 与Java数组的互转

Scal数组转Java数组:

val arr4 = ArrayBuffer("1", "2", "3")

//Scala to Java

import scala.collection.JavaConversions.bufferAsJavaList

val javaArr = new ProcessBuilder(arr4)

println(javaArr.command())

Java数组转Scala数组:

import scala.collection.JavaConversions.asScalaBuffer

import scala.collection.mutable.Buffer

val scalaArr: Buffer[String] = javaArr.command()

println(scalaArr)

6) 数组的遍历

for(x <- arr1) {

  println(x)

}

 

5.3  元组 Tuple

元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。

1)  元组的创建

val tuple1 = (1, 2, 3, "heiheihei")

println(tuple1)

2)  元组数据的访问,注意元组元素的访问有下划线,并且访问下标从1开始,而不是0

val value1 = tuple1._4

println(value1)

3)  元组的遍历

方式1:

for (elem <- tuple1.productIterator) {

  print(elem)

}

println()

方式2:

tuple1.productIterator.foreach(i => println(i))

tuple1.productIterator.foreach(print(_))

5.4  列表 List

如果List列表为空,则使用Nil来表示。

1)  创建List

val list1 = List(1, 2)

println(list1)

2)  访问List元素

val value1 = list1(1)

println(value1)

3)  List元素的追加

val list2 = list1 :+ 99

println(list2)

val list3 = 100 +: list1

println(list3)

4)  List的创建与追加,符号“::”,注意观察去掉Nil和不去掉Nil的区别

val list4 = 1 :: 2 :: 3 :: list1 :: Nil

println(list4)

包含Nil : List[Any] = List(1, 2, 3, List(1, 2))

不包含:List[Int] = List(1, 2, 3, 1, 2)

5.5  队列 Queue

队列数据存取符合先进先出策略

1)  队列的创建

import scala.collection.mutable

val q1 = new mutable.Queue[Int]

println(q1)

//输出结果: Queue()

2)  队列元素的追加

q1 += 2;//2位元素内容

println(q1)

//输出结果:Queue(2)

3)  向队列中追加List

q1 ++= List(2, 3, 4)

println(q1)

//输出结果:Queue(2,2,3,4)

4)  按照进入队列的顺序删除元素

q1.dequeue()

println(q1)

5)  塞入数据

//只能在后面追加元素?

q1.enqueue(9, 8, 7)

println(q1)

6)  返回队列的第一个元素

println(q1.head)

7)  返回队列最后一个元素

println(q1.last)

8)  返回除了第一个以外的元素

println(q1.tail)

5.6  映射 Map

这个地方的学习,就类比Java的map集合学习即可。

1)  构造不可变映射

val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)

2)  构造可变映射

val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)

3)  空的映射

val map3 = new scala.collection.mutable.HashMap[String, Int]

4)  对偶元组

val map4 = Map(("Alice", 10), ("Bob", 20), ("Kotlin", 30))

5)  取值

如果映射中没有值,则会抛出异常,使用contains方法检查是否存在key。如果通过 映射.get(键) 这样的调用返回一个Option对象,要么是Some,要么是None。

val value1 = map1("Alice")//建议使用get方法得到map中的元素

println(value1)

6)  更新值

map2("Alice") = 99

println(map2("Alice"))

或:

map2 += ("Bob" -> 99)

map2 -= ("Alice", "Kotlin")

println(map2)

或:

val map5 = map2 + ("AAA" -> 10, "BBB" -> 20)

println(map5)

7)  遍历

for ((k, v) <- map1) println(k + " is mapped to " + v)

for (k <- map1.keys) println(k)

for (v <- map1.values) println(v)

for(v <- map1) prinln(v)

5.7  集 Set

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

如果想要按照已排序的顺序来访问集中的元素,可以使用SortedSet(已排序数据集),已排序的数据集是用红黑树实现的。

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

1)  Set不可变集合的创建

val set = Set(1, 2, 3)

println(set)

2)  Set可变集合的创建,如果import了可变集合,那么后续使用默认也是可变集合

import scala.collection.mutable.Set

val mutableSet = Set(1, 2, 3)

3)  可变集合的元素添加

mutableSet.add(4)

mutableSet += 6

// 注意该方法返回一个新的Set集合,而非在原有的基础上进行添加

mutableSet.+(5)

4)  可变集合的元素删除

mutableSet -= 1

mutableSet.remove(2)//括号内是元素内容,而非元素索引

println(mutableSet)

5) 遍历

for(x <- mutableSet) println(x)

 

序号

方法

描述

1

def +(elem: A): Set[A]

为集合添加新元素,并创建一个新的集合,除非元素已存在

2

def -(elem: A): Set[A]

移除集合中的元素,并创建一个新的集合

3

def contains(elem: A): Boolean

如果元素在集合中存在,返回 true,否则返回 false。

4

def &(that: Set[A]): Set[A]

返回两个集合的交集

5

def &~(that: Set[A]): Set[A]

返回两个集合的差集

6

def ++(elems: A): Set[A]

合并两个集合

7

def drop(n: Int): Set[A]]

返回丢弃前n个元素新集合

8

def dropRight(n: Int): Set[A]

返回丢弃最后n个元素新集合

9

def dropWhile(p: (A) => Boolean): Set[A]

从左向右丢弃元素,直到条件p不成立

10

def max: A

查找最大元素

11

def min: A

查找最小元素

12

def take(n: Int): Set[A]

返回前 n 个元素

6) Set更多常用操作

5.8  集合元素与函数的映射

1) map:将集合中的每一个元素映射到某一个函数

val names = List("Alice", "Bob", "Nick")

println(names.map(_.toUpperCase))

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

val names = List("Alice", "Bob", "Nick")

println(names.flatMap(_.toUpperCase()))

5.9  化简、折叠、扫描

1) 折叠,化简:将二元函数引用于集合中的函数

val list = List(1, 2, 3, 4, 5)

val i1 = list.reduceLeft(_ - _)

val i2 = list.reduceRight(_ - _)

println(i1)

println(i2)

.reduceLefft(_ - _)这个函数的执行逻辑如图所示:

.reduceRight(_ - _)反之同理

2)  折叠,化简:fold

fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历。可以把reduceLeft看做简化版的foldLeft。相关函数:fold,foldLeft,foldRight,可以参考reduce的相关方法理解。

val list2 = List(1, 9, 2, 8)

val i4 = list2.fold(5)((sum, y) => sum + y)

println(i4)

//初始值是5  ,  5+1+9+2+8=25

foldRight

val list3 = List(1, 9, 2, 8)

val i5 = list3.foldRight(100)(_ - _)

println(i5)

//1-(9-(2-(8-100)))

尖叫提示:foldLeft和foldRight有一种缩写方法对应分别是:/:和:\

例如:

foldLeft

val list4 = List(1, 9, 2, 8)

val i6 = (0 /: list4)(_ - _)

println(i6)

//0-1-9-2-8

3)  统计一句话中,各个文字出现的次数

val sentence = "一首现代诗《笑里藏刀》:哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈刀哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈"

//m +  (“一” -> 1, “首” -> 1, “哈” -> 1)

val i7 = (Map[Char, Int]() /: sentence)((m, c) => m + (c -> (m.getOrElse(c, 0) + 1)))

println(i7)

//result = Map(现 -> 1, 首 -> 1, 》 -> 1, 一 -> 1, 诗 -> 1, 哈 -> 34, 里 -> 1, 刀 -> 2, 笑 -> 1, 代 -> 1, 藏 -> 1, : -> 1, 《 -> 1)

4)  折叠,化简,扫描

这个理解需要结合上面的知识点,扫描,即对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存。

val i8 = (1 to 10).scanLeft(0)(_ + _)

println(i8)

//打印的是折叠的中的过程

5.10  拉链

val list1 = List("15837312345", "13737312345", "13811332299")

val list2 = List(17, 87)

println(list1 zip list2)

//木桶原理:根据短的来,最后的138舍弃

5.11  迭代器

你可以通过iterator方法从集合获得一个迭代器,通过while循环和for表达式对集合进行遍历。

val iterator = List(1, 2, 3, 4, 5).iterator

while (iterator.hasNext) {

  println(iterator.next())

}

或:

for(enum <- iterator) {

  println(enum)

}

5.12  流 Stream

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

1)  使用#::得到一个stream

def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)

2)  传递一个值,并打印stream集合

val tenOrMore = numsForm(10)

println(tenOrMore)

3)  tail的每一次使用,都会动态的向stream集合按照规则生成新的元素

println(tenOrMore.tail)

println(tenOrMore)

4)  使用map映射stream的元素并进行一些计算

println(numsForm(5).map(x => x * x))

5.13  视图 View

Stream的懒执行行为,你可以对其他集合应用view方法来得到类似的效果,该方法产出一个其方法总是被懒执行的集合。但是view不会缓存数据,每次都要重新计算。

例如:我们找到10万以内,所有数字倒序排列还是它本身的数字。

val viewSquares = (1 to 100000).view.map(x => x).filter(x => { x.toString == x.toString.reverse})

println(viewSquares.mkString(“ ”))

for(x <- viewSquares){

  print(x + ",")

}

5.14  线程安全的集合

所有线程安全的集合都是以Synchronized开头的集合,例如:

SynchronizedBuffer

SynchronizedMap

SynchronizedPriorityQueue

SynchronizedQueue

SynchronizedSet

SynchronizedStack

5.15  并行集合

Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。

主要用到的算法有:

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

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

1)  打印1~5

(1 to 5).foreach(println(_))

println()

(1 to 5).par.foreach(println(_))

2)  查看并行集合中元素访问的线程

val result1 = (0 to 10000).map{case _ => Thread.currentThread.getName}.distinct

val result2 = (0 to 10000).par.map{case _ => Thread.currentThread.getName}.distinct

println(result1)

println(result2)

5.16 操作符

这部分内容没有必要刻意去理解和记忆,语法使用的多了,自然就会产生感觉,该部分内容暂时大致了解一下即可。

1)  如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号反引号:

val `val` = 42

2)  这种形式叫中置操作符,A操作符B等同于A.操作符(B)

3)  后置操作符,A操作符等同于A.操作符,如果操作符定义的时候不带()则调用时不能加括号

4)  前置操作符,+、-、!、~等操作符A等同于A.unary_操作符

5)  赋值操作符,A操作符=B等同于A=A操作符B

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值