尚硅谷Scala (10-11)

十、数据结构()-集合

10.1 数据结构特点

10.1.1 scala 集合基本介绍

uml => 统一建模语言
1) Scala 同时支持不可变集合和可变集合,不可变集合可以安全的并发访问
两个主要的包:
不可变集合: scala.collection.immutable
可变集合: scala.collection.mutable
2) Scala 默认采用不可变集合,对于几乎所有的集合类, Scala 都同时提供了可变 (mutable) 和不可
(immutable) 的版本
3) Scala 的集合有三大类:序列 Seq( 有序的 ,Linear Seq) 、集 Set 、映射 Map key->value 】,所有的集合都扩展自 Iterable 特质,在 Scala 中集合有可变( mutable )和不可变( immutable )两种类型。

10.1.2 可变集合和不可变集合举例

1) 不可变集合: scala 不可变集合,就是这个 集合本身不能动态变化 ( 类似 java 的数组,是不可
以动态增长的 )
2) 可变集合 : 可变集合,就是这个 集合本身可以动态变化的 ( 比如 :ArrayList , 是可以动态增长的 )
3) 使用 java 做了一个简单的案例说明
public class JavaCollection {
    public static void main(String[] args) {
//不可变集合类似 java 的数组
        int[] nums = new int[3];
        nums[2] = 11; //?
        nums[2] = 22;
//nums[3] = 90; //?
// String[] names = {"bj", "sh"};
// System.out.println(nums + " " + names);
//
// //可变集合举例
        ArrayList al = new ArrayList();
        al.add("zs");
        al.add("zs2");
        System.out.println(al + " 地址= " + al.hashCode()); //地址
        al.add("zs3");
        System.out.println(al + " 地址 2=" + al.hashCode()); //地址
    }
}

10.2 不可变集合继承层次一览图

10.2.1

尚硅谷Scala (10-11)_第1张图片

10.2.2 小结

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 是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找,它的价值在于应用到一些具体的应用场景 ( 电商网站 , 大数据推荐系统 : 最近浏览的 10
个商品 )

10.3 可变集合继承层次一览图

10.3.1

尚硅谷Scala (10-11)_第2张图片

 10.3.2 对上图的说明

1) 在可变集合中比不可变集合更加丰富
2) Seq 集合中, 增加了 Buffer 集合,将来开发中,我们常用的有 ArrayBuffer ListBuffer
3) 如果涉及到线程安全可以选择使用 syn.. 开头的集合
4) 其它的说明参考不可变集合

10.4 数组-定长数组(声明泛型)

10.4.1 第一种方式定义数组

说明
这里的数组等同于 Java 中的数组 , 中括号的类型就是数组的类型
val arr1 = new Array[Int](10)
// 赋值 , 集合元素采用小括号访问
arr1(1) = 7
object ArrayDemo01 {
  def main(args: Array[String]): Unit = {
    //说明
    //1. 创建了一个 Array 对象, //2. [Int] 表示泛型,即该数组中,只能存放 Int
    //3. [Any] 表示 该数组可以存放任意类型
    //4. 在没有赋值情况下,各个元素的值 0
    //5. arr01(3) = 10 表示修改 第 4 个元素的值
    val arr01 = new Array[Int](4)
    println(arr01.length) //4

    println("arr01(0)="+arr01(0)) //0

    for(i <- arr01){
      println(i)
    }
    println("=====================")
    arr01(3)=10
    for(i <- arr01){
      println(i)
    }
  }
}

10.4.2 第二种方式定义数组

说明
在定义数组时,直接赋值
// 使用 apply 方法创建数组对象
val arr1 = Array(1, 2)
object ArrayDemo02 {
  def main(args: Array[String]): Unit = {
    //说明
    //1. 使用的是 object Array 的 apply
    //2. 直接初始化数组,这时因为你给了 整数和 "", 这个数组的泛型就 Any
    //3. 遍历方式一样
    var arr02 =Array(1,3,"zxc")
    arr02(0)="zhangshan"
    arr02(2)=123
    for(i <- arr02) {
      println(i)
      //      zhangshan
      //      3
      //      123
    }
      //可以使用我们传统的方式遍历,使用下标的方式遍历
      for(index <- 0 to arr02.length-1){
        printf("arr02[%d]=%s",index,arr02(index)+"\t")
      }
    }
}

10.5 数组-变长数组(声明泛型)

说明
// 定义 / 声明
val arr2 = ArrayBuffer[Int]()
// 追加值 / 元素
arr2.append(7)
// 重新赋值
arr2(0) = 7
object ArrayBufferDemo01 {
  def main(args: Array[String]): Unit = {
    val arr01 = ArrayBuffer[Any](3,2,5)

    println("arr01(1)="+arr01(1)) //2
    for(i <- arr01){
      println(i)
    }

    println(arr01.length) //3
    println("arr01.hash="+arr01.hashCode()) //110266112
    //使用 append 追加数据 ,append 支持可变参数
    //可以理解成 java 的数组的扩容
    arr01.append(90.0,13)
    println("arr01.hash="+arr01.hashCode()) //-70025354

    arr01(1)=89
    println("==================")
    for(i <-arr01){
      println(i)  //3,89,5,90.0,13
    }

    arr01.remove(0)
    println("--------删除后的元素遍历---------------")
    for(i <- arr01){
      println(i)  //89,5,90.0,13
    }

    println("最新的长度=" + arr01.length)  //4
  }
}

10.5.1 变长数组分析小结

1) ArrayBuffer 是变长数组,类似 java ArrayList
2) val arr2 = ArrayBuffer[Int]() 也是使用的 apply 方法构建对象
3) def append(elems: A*) { appendAll(elems) } 接收的是可变参数 .
4) append 一次, arr 在底层会重新分配空间,进行扩容, arr2 的内存地址会发生变化,也就成
为新的 ArrayBuffer

10.5.2 定长数组与变长数组的转换

说明
在开发中,我们可能使用对定长数组和变长数组,进行转换
arr1. toBuffer // 定长数组转可变数组
arr2. toArray // 可变数组转定长数组
注意事项:
arr2.toArray 返回结果才是一个定长数组, arr2 本身没有变化
arr1.toBuffer 返回结果才是一个可变数组, arr1 本身没有变化
object Array22ArrayBuffer {
  def main(args: Array[String]): Unit = {
    val arr2 = ArrayBuffer[Int]()
    arr2.append(1,2,3)
    println(arr2)

    //说明
    //1. arr2.toArray 调用 arr2 的方法 toArray
    //2. 将 ArrayBuffer ---> Array
    //3. arr2 本身没有任何变化
    val newArr = arr2.toArray
    println(newArr)

    //说明
    //1. newArr.toBuffer 是把 Array->ArrayBuffer
    //2. 底层的实现
    /*
      override def toBuffer[A1 >: A]: mutable.Buffer[A1] = {
        val result = new mutable.ArrayBuffer[A1](size)
        copyToBuffer(result)
        result
      }
    */
    //3. newArr 本身没变化
    val newArr2 = newArr.toBuffer
    newArr2.append(123)
    println(newArr2)
  }
}

10.5.3 多维数组的定义和使用

说明
// 定义
val arr = Array.ofDim[Double](3,4)
// 说明:二维数组中有三个一维数组,
每个一维数组中有四个元素
// 赋值
arr(1)(1) = 11.11
object MultiplyArray {
  def main(args: Array[String]): Unit = {
    //创建
    val arr =Array.ofDim[Int](3,4)
    //遍历
    for(item <- arr){//取出二维数组的各个元素(一维数组)
      for(item2 <- item){// 元素(一维数组) 遍历
          print(item2+"\t")
      }
      println()
    }
    //指定取出
    arr(1)(1)=6
    println(arr(1)(1))

    //传统遍历
    for(i <- 0 to arr.length-1){
      for(j <- 0 to arr(i).length-1){
        printf("arr[%d][%d]=%d\t",i,j,arr(i)(j))
      }
      println()
    }
  }
}

10.6 数组-Scala 数组与 Java List 的互转

10.6.1 Scala 数组转 Java List

在项目开发中,有时我们需要将 Scala 数组转成 Java 数组

10.6.2 演示的代码

object ArrayBuffer2JavaList {
  def main(args: Array[String]): Unit = {
    // Scala 集合和 Java 集合互相转换
    val arr = ArrayBuffer("1", "2", "3")
    /*
  implicit def bufferAsJavaList[A](b : scala.collection.mutable.Buffer[A]) : java.util.List[A] = { /*
  compiled code */ }
  */
    import scala.collection.JavaConverters.bufferAsJavaList
    //对象 ProcessBuilder , 因为 这里使用到上面的 bufferAsJavaList
    val javaArr = new ProcessBuilder(arr) //为什么可以这样使用?
    // 这里 arrList 就是 java 中的 List
    val arrList = javaArr.command()
    println(arrList) //输出 [1, 2, 3]
  }
}

10.6.3 补充了一个多态(使用 trait 来实现的参数多态)的知识点

trait MyTrait01 {}
class A extends MyTrait01 {}
object B {
def test(m: MyTrait01): Unit = {
println("b ok..")
}
}
//明确一个知识点
//当一个类继承了一个 trait
//那么该类的实例,就可以传递给这个 trait 引用
val a01 = new A
B.test(a01)

10.6.4 Java List Scala 数组(mutable.Buffer)

//java 的 List 转成 scala 的 ArrayBuffer
//说明
//1. asScalaBuffer 是一个隐式函数
/*
implicit def asScalaBuffer[A](l : java.util.List[A]) : scala.collection.mutable.Buffer[A] = { /* compiled
code */ }
*/
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable
// java.util.List ==> Buffer
val scalaArr: mutable.Buffer[String] = arrList
scalaArr.append("jack")
scalaArr.append("tom")
scalaArr.remove(0)
println(scalaArr) // (2,3,jack,tom)

10.7 元组 Tuple-元组的基本使用

10.7.1 基本介绍

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

10.7.2 元组的创建

object TupleDemo01 {
  def main(args: Array[String]): Unit = {
    //创建
    //说明 1. tuple1 就是一个 Tuple 类型是 Tuple5
    //简单说明: 为了高效的操作元组 , 编译器根据元素的个数不同,对应不同的元组类型
    // 分别 Tuple1----Tuple22
    val tuple1=(1,2,3,"hello",4)
    println(tuple1)
  }
}

10.8 元组 Tuple-元组数据的访问

10.8.1 基本介绍

访问元组中的数据 , 可以采用顺序号( _ 顺序号),也可以通过索引( productElement )访问。

10.8.2 应用案例

   //遍历和访问元组
    val t1=(1,"a","b",true,2)
    println(t1._1)// 1 //访问元组的第一个元素 ,从 1 开始
    println(t1.productElement(0))// 0 // 访问元组的第一个元素,从 0 开始
    //原理
    /*
    override def productElement(n: Int) = n match {
    case 0 => _1
    case 1 => _2
    case 2 => _3
    case 3 => _4
    case 4 => _5
    case _ => throw new IndexOutOfBoundsException(n.toString())
}
*/

10.9 元组 Tuple-元组数据的遍历

    println("==================遍历元组=========================")
    //遍历元组, 元组的遍历需要使用到迭代器
    for(item <- t1.productIterator){
      println("item="+item)
    }

10.10 列表 List-创建 List

10.10.1 基本介绍

Scala 中的 List Java List 不一样,在 Java List 是一个接口,真正存放数据是 ArrayList ,而 Scala
List 可以直接存放数据,就是一个 object ,默认情况下 Scala List 是不可变的, List 属于序列 Seq
        val List = scala.collection.immutable.List
        object List extends SeqFactory[List]

10.10.2 创建 List 的应用案例

object ListDemo01 {
  def main(args: Array[String]): Unit = {
    //说明
    //1. 在默认情况下 List 是 scala.collection.immutable.List,即不可变
    //2. 在 scala 中,List 就是不可变的,如需要使用可变的 List,则使用 ListBuffer
    //3. List 在 package object scala 做了 val List = scala.collection.immutable.List
    //4. val Nil = scala.collection.immutable.Nil // List()

    val list01=List(1,2,3)//创建时,直接分配元素
    println(list01)
    val list02=Nil   //空集合
    println(list02)
  }
}

10.10.3 创建 List 的应用案例小结

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

10.11 列表 List-访问 List 元素

//访问 List 的元素
val value1 = list01(1) // 1 是索引,表示取出第 2 个元素. 
println("value1=" + value1) // 2

10.12 列表 List-元素的追加

10.12.1 基本介绍

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

10.12.2 方式 1-在列表的最后增加数据

10.12.3 方式 2-在列表的最前面增加数据

10.12.4 方式 3-在列表的最后增加数据

说明 :
1) 符号 :: 表示向集合中 新建集合添加元素。
2) 运算时, 集合对象一定要放置在最右边
3) 运算规则,从右向左。
4) ::: 运算符是将集合中的每一个元素加入到集合中
    println("-------------list 追加元素后的效果-----------------")
    //通过 :+ 和 +: 给 list 追加元素(本身的集合并没有变化)
    var list1=List(1,2,3,4,"hello")
    //在元素的最后加
    val list2=list1:+5
    println(list1)  //list1 没有变化 (1, 2, 3, "abc"),说明 list1 还是不可变
    println(list2)  //新的列表结果是 (1, 2, 3, "abc", 4)

    //在元素的前面加
    val list3=6+:list1
    println(list3)  //List(6, 1, 2, 3, 4, hello)

    //::符号的使用
    val list4=List(1,2,3,4,"zxc")
    val list5=4::5::6::list4::Nil
    println(list5)
    //说明 val list5 = 4 :: 5 :: 6 :: list4 :: Nil 步骤
    //1. List()
    //2. List(List(1, 2, 3, "abc"))
    //3. List(6,List(1, 2, 3, "abc"))
    //4. List(5,6,List(1, 2, 3, "abc"))
    //5. List(4,5,6,List(1, 2, 3, "abc"))

    val list6=4::5::6::list4:::Nil
    println(list6)
    //说明 val list6 = 4 :: 5 :: 6 :: list4 ::: Nil 步骤
    //1. List()
    //2. List(1, 2, 3, "abc")
    //3. List(6,1, 2, 3, "abc")
    //4. List(5,6,1, 2, 3, "abc")
    //5. List(4,5,6,1, 2, 3, "abc")

10.13 ListBuffer

10.13.1 基本介绍

ListBuffer 是可变的 list 集合,可以添加,删除元素 ,ListBuffer 属于序列
// 追一下继承关系即可
Seq var listBuffer = ListBuffer(1,2)

10.13.2 应用实例代码

object ListBufferDemo01 {
  def main(args: Array[String]): Unit = {
    //创建ListBuffer
    val lst0=ListBuffer[Int](1,2,3,4)

    //如何访问
    println("lst0(2)="+lst0(2))
    for(item <- lst0){// 遍历,是有序
      println("item="+item)
    }

    //动态的增加元素,lst1 就会变化, 增加一个一个的元素
    val lst1=new ListBuffer[Int]//空的 ListBuffer
    lst1+=4// lst1 (4)
    lst1.append(5)// list1(4,5)

    lst0 ++= lst1// lst0 (1, 2, 3,4,4,5)
    for(item <- lst0){// 遍历,是有序
      print("item="+item+"\t")
    }
    val lst2=lst0++lst1 // lst2(1, 2, 3,4,4,5,4,5)
    val lst3=lst0:+5   //lst0 不变 lst3 (1, 2, 3,4,4,5,5)
    println("lst3=" + lst3)

    println("=====删除=======")
    println("lst1=" + lst1)
    lst1.remove(1) // 表示将下标为 1 的元素删除
    for (item <- lst1) {
      println("item=" + item) //4
    }
  }
}

10.14 队列 Queue-基本介绍

10.14.1 队列的应用场景

银行排队的案例

10.14.2 队列的说明

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

10.15 队列 Queue-队列的创建

object QueueDemo01 {
  def main(args: Array[String]): Unit = {
    val q1 =new mutable.Queue[Int]
    println(q1)
  }
}

10.16 队列 Queue-队列元素的追加数据

  //给队列增加元素
    q1+=20
    println("q1="+q1) //q1=Queue(20)
    q1++=List(1,2,3)  // 默认值直接加在队列后面
    println("q1="+q1) //q1=Queue(20, 1, 2, 3)

    //q1 += List(10,0) // 表示将 List(10,0) 作为一个元素加入到队列中,

10.17 队列 Queue-删除和加入队列元素

在队列中,严格的遵守,入队列的数据,放在队位,出队列的数据是队列的头部取出

    //dequeue 从队列的头部取出元素 q1 本身会变
    val queueElement=q1.dequeue()
    println(queueElement)
    println("q1="+q1)

    //enQueue 入队列,默认是从队列的尾部加入. Redis
    q1.enqueue(100,10,100,888)
    println("q1="+q1) //q1=Queue(1, 2, 3, 100, 10, 100, 888)

10.18 队列 Queue-返回队列的元素

   println("============Queue-返回队列的元素=================")
    //队列 Queue-返回队列的元素
    //1. 获取队列的第一个元素
    println(q1.head)// 1, 对 q1 没有任何影响

    //2. 获取队列的最后一个元素
    println(q1.last)  //888,对 q1 没有任何影响

    //3. 取出队尾的数据 ,即:返回除了第一个以外剩余的元素,可以级联使用
    println(q1.tail)  //Queue(2, 3, 100, 10, 100, 888)
    println(q1.tail.tail.tail.tail) //Queue(10, 100, 888)

10.19 映射 Map-基本介绍

10.19.1 Java 中的 Map 回顾

HashMap 是一个 散列表 ( 数组 + 链表 ) ,它存储的内容是键值对 ( key-value) 映射, Java 中的 HashMap 是无序的 key 不能重复

10.19.2 应用案例

public class JavaHashMap {
    public static void main(String[] args) {
        HashMap hm = new HashMap();
        hm.put("no1",100);
        hm.put("no2",200);
        hm.put("no3",300);
        hm.put("no4",400);

        System.out.println(hm); //无序的
        System.out.println(hm.get("no2"));
    }
}

10.19.3 Scala 中的 Map 介绍

1) Scala 中的 Map Java 类似,也是一 个散列表 ,它存储的内容也是键值对 (key-value) 映射, Scala 中不可变的 Map 是有序的 可变的 Map 是无序的
2) Scala中,有可变Map (scala.collection.mutable.Map) 和不k可变Map(scala.collection.immutable.Map)

10.20映射 Map-构建 Map

10.20.1 方式 1-构造不可变映射

Scala 中的不可变 Map 是有序,构建 Map 中的元素底层是 Tuple2 类型。
object MapDemo01 {
  def main(args: Array[String]): Unit = {
    //1.默认 Map 是 immutable.Map
    //2.key-value 类型支持 Any
    //3.在 Map 的底层,每对 key-value 是 Tuple2
    //4.从输出的结果看到,输出顺序和声明顺序一致
    val map1 =Map("Alice"->10,"Bob"->20,"kotlin"->"北京")
    println(map1)
  }
}

10.20.2 方式 2-构造可变映射

    //方式2-构造可变映射
    //1.从输出的结果看到,可变的 map 输出顺序和声明顺序不一致
    val map2 = mutable.Map("Alice" -> 10, "Bob" -> 20, "kotlin" -> "上海")
    println(map2)

10.20.3 方式 3-创建空的映射

    val map3 = new mutable.HashMap[String, Int]
    println("map3="+map3)

10.20.4 方式 4-对偶元组

说明:
即创建包含键值对的二元组, 和第一种方式等价,只是形式上不同而已。
对偶元组 就是只含有两个数据的元组。
    val map4 = mutable.Map(("zxc", 10), ("www", 20), ("Bob", "广州"))
    println("map4"+map4)

10.21 映射 Map-取值

10.21.1 方式 1-使用 map(key)

        val value1 = map2("Alice")
        println(value1)
说明 :
1) 如果 key 存在,则返回对应的值
2) 如果 key 不存在,则抛出异常 [java.util.NoSuchElementException]
3) Java , 如果 key 不存在则返回 null
//方式 1-使用 map(key)
println(map4("Alice")) // 10
//抛出异常(java.util.NoSuchElementException: key not found:)
//println(map4("Alice~"))

10.21.2 方式 2-使用 contains 方法检查是否存在 key

// 返回 Boolean
// 1. 如果 key 存在,则返回 true
// 2. 如果 key 不存在,则返回 false
map4.contains("B")
说明 :
使用 containts 先判断在取值,可以防止异常,并加入相应的处理逻辑
    if(map4.contains("zxcx")){
      println("key存在,值="+map4("zxcx"))
    }else{
      println("key不存在")
    }

10.21.3 方式 3-使用 map.get(key).get 取值

通过 映射 .get( ) 这样的调用返回一个 Option 对象,要么是 Some ,要么是 None
说明和小结 :
1) map.get 方法会将数据进行包装
2) 如果 map.get(key) key 存在返回 some, 如果 key 不存在,则返回 None
3) 如 果 map.get(key).get key 存 在 , 返 回 key 对 应 的 值 , 否 则 , 抛 出 异 常
java.util.NoSuchElementException: None.get
    println(map4.get("zxc").get)  //10
    println(map4.get("zxcx").get)  //抛出异常

10.21.4 方式 4-使用 map4.getOrElse()取值

getOrElse 方法 : def getOrElse[V1 >: V](key: K, default: => V1)
说明:
1) 如果 key 存在,返回 key 对应的值。
2) 如果 key 不存在,返回默认值。在 java 中底层有很多类似的操作
    println(map4.getOrElse("zxc","是一个明星"))  //10
    println(map4.getOrElse("zxcc","是什么东西")) //是什么东西

10.21.5 如何选择取值的方式

1) 如果我们确定 map 有这个 key , 则应当使用 map(key), 速度快
2) 如果我们不能确定 map 是否有 key , 而且有不同的业务逻辑,使用 map.contains() 先判断在加入逻辑
3) 如果只是简单的希望得到一个值,使用 map4.getOrElse("ip","127.0.0.1")

10.22 映射 Map-map 修改、添加和删除

10.22.1 更新 map 的元素

    val map5 = mutable.Map(("A", 1), ("B", 2), ("C","北京"))
    map5("AA")=20 //不存在,就添加
    println(map5) //HashMap(AA -> 20, A -> 1, B -> 2, C -> 北京)
    map5("A")=100 //存在,就修改
    map5.put("D",888)
    println(map5) //HashMap(AA -> 20, A -> 100, B -> 2, C -> 北京)
小结
1) map 是可变的,才能修改,否则报错
2) 如果 key 存在:则修改 对应的值 , key 不存在 , 等价于添加 一个 key-val

10.22.2 添加 map 元素

尚硅谷Scala (10-11)_第3张图片

说明: 当增加一个 key-value ,如果 key 存在就是更新,如果不存在,这是添加

10.22.3 删除 map 元素 

    map5.remove("AA")
    map5-=("D","A")
    println(map5)
说明
1) "A","B" 就是要删除的 key, 可以写多个 .
2) 如果 key 存在,就删除,如果 key 不存在,也不会报错 .

10.23 映射 Map-map 遍历

map 的元素 ( 元组 Tuple2 对象 ) 进行遍历的方式很多,具体如下
val map1 = mutable.Map( ("A", 1), ("B", " 北京 "), ("C", 3) )
for ((k, v) <- map1) println(k + " is mapped to " + v)
for (v <- map1.keys) println(v)
for (v <- map1.values) println(v)
for(v <- map1) println(v) //v Tuple?
说明
1. 每遍历一次,返回的元素是 Tuple2
2. 取出的时候,可以按照元组的方式来取
    //map的遍历
    val map6 = mutable.Map(("A", 1), ("B", 100), ("C", "杭州"))
    for((k,v) <- map6) println(k+" is mapped to "+v)  //A is mapped to 1 ......
    for(v <- map6.keys) println(v)  //A B C
    for(v <- map6.values) println(v)  //1 100 杭州

    //这样取出方式 v 类型是 Tuple2
    for(v <- map6) println(v+" key="+v._1+" val="+v._2) //(A,1) key=A val=1

10.24 Set-基本介绍

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

10.24.1 Java Set 的回顾

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

10.24.2 案例演示

public class JavaHashSet {
public static void main(String[] args) {
    //java 中的 Set 的元素 没有顺序,不能重复
    HashSet hs = new HashSet();
    hs.add("jack");
    hs.add("tom");
    hs.add("jack");
    hs.add("jack2");
    System.out.println(hs);    //jack2 jack tom

}
}

Scala 中 Set 的说明

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

10.25 Set-创建

object SetDemo01 {
  def main(args: Array[String]): Unit = {
    val set = Set(1, 2, 4)  //不可变
    println(set)  //Set(1, 2, 4)

    val set2 = mutable.Set(1, 2, "hello")    //可变
    println(set2) //HashSet(1, 2, hello)
  }
}

10.26 Set-可变集合的元素添加、删除、遍历

    //前提是可变的
    //添加
    set2+=100 //方式一
    set2+=(5) //方式二
    set2.add("world") //方式三
    println(set2) //HashSet(1, 2, world, 100, 5, hello)

    //删除
    set2-=2
    set2.remove(100)
    println(set2) //HashSet(1, world, 5, hello)

    //遍历
    for(item <- set2) println(item) //1 world 5 hello

十一、数据结构()-集合操作

11.1 集合元素的映射-map 映射操作

11.1.1 看一个实际需求

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

11.1.2 map 映射操作

尚硅谷Scala (10-11)_第4张图片

 11.1.3 使用传统方法

object MapOperateDemo01 {
  def main(args: Array[String]): Unit = {
    val list1 = List(1, 2, 3, 4)
    var list2 = List[Int]()
    for(item <- list1){
      list2 = list2:+ item*2
    }
    println(list2)  //List(2, 4, 6, 8)

    //对上面传统的方法来解决问题的小结
    //1. 优点
    //(1) 处理方法比较直接,好理解
    //2. 缺点
    // (1) 不够简洁,高效
    // (2) 没有体现函数式编程特点 集合=》函数 => 新的集合 =》 函数 .. // 
    // (3) 不利于处理复杂的数据处理业务
  }
}

11.1.4 高阶函数基本使用案例 1

object HighOrderFunDemo01 {
  def main(args: Array[String]): Unit = {
    //使用高阶函数
    val res = test(sum2, 3.5)
    println(res)  //7.0

    //在 scala 中,可以把一个函数直接赋给一个变量,但是不执行函数
    val f1 = myPrint _
    f1() //执行
  }

  def myPrint(): Unit = {
    println("hello,world!")
  }

  //说明
  //1. test 就是一个高阶函数
  //2. f: Double => Double 表示一个函数, 该函数可以接受一个 Double,返回 Double
  //3. n1: Double 普通参数
  //4. f(n1) 在 test 函数中,执行 你传入的函数
  def test(f:Double => Double, n1: Double)={
    f(n1)
  }

  //普通的函数, 可以接受一个 Double,返回 Double
  def sum2(d:Double):Double={
    d+d
  }
}

11.1.5 高阶函数应用案例 2

object HighOrderFunDemo02 {
  def main(args: Array[String]): Unit = {
    test2(sayOk)
  }

  //说明 test2 是一个高阶函数,可以接受一个 没有输入,返回为 Unit 的函数
  def test2(f:() =>Unit)={
    f()
  }

  def sayOk()={
    println("sayOkok")
  }
  
  def sub(n1:Int)={
  }
}

11.1.6 使用 map 映射函数来解决

object MapOperateDemo02 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)
    val list2 = list.map(multiple)
    //说明 list.map(multiple) 做了什么
    //1. 将 list 这个集合的元素 依次遍历
    //2. 将各个元素传递给 multiple 函数 => 新 Int
    //3. 将得到新 Int ,放入到一个新的集合并返回
    //4. 因此 multiple 函数调用 4
    println("list2="+list2) //list2=List(2, 4, 6, 8)
  }

  def multiple(n:Int):Int={
    2*n
  }
}

11.1.7 深刻理解 map 映射函数的机制-模拟实现

object MapOperateDemo02 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)
    val list2 = list.map(multiple)
    //说明 list.map(multiple) 做了什么
    //1. 将 list 这个集合的元素 依次遍历
    //2. 将各个元素传递给 multiple 函数 => 新 Int
    //3. 将得到新 Int ,放入到一个新的集合并返回
    //4. 因此 multiple 函数调用 4
    println("list2="+list2) //list2=List(2, 4, 6, 8)

    //深刻理解 map 映射函数的机制-模拟实现
    val myList=MyList()
    val myList2 = myList.map(multiple)
    println("myList2="+myList2)

  }

  def multiple(n:Int):Int={
    2*n
  }
}

//深刻理解 map 映射函数的机制-模拟实现
class MyList{
  val list1=List(1,2,3,4)
  //新的集合
  var list2 = List[Int]()

  //写map
  def map(f:Int => Int):List[Int]={
    //遍历集合
    for(item <- this.list1){
      //过滤,扁平化....
      list2=list2:+f(item)
    }
    list2
  }
}

object MyList{
  def apply():MyList=new MyList()
}

11.1.8 课堂练习

object Exercise01 {
  def main(args: Array[String]): Unit = {
    val names =List("Alice","Bob","Nick")
    val name2 = names.map(upper)
    println(name2)    //List(ALICE, BOB, NICK)
    
  }

  def upper(s:String): String ={
    s.toUpperCase
  }
}

11.1.9 flatmap 映射:flat 即压扁,压平,扁平化映射

扁平化说明
flatmap flat 即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返
回新的集合。
object FlatMapDemo01F {
  def main(args: Array[String]): Unit = {
    val names = List("Alice", "Bob", "Nick")
    //需求是将 List 集合中的所有元素,进行扁平化操作,即把所有元素打散
    val names2 = names.flatMap(upper)
    println("names2=" + names2) //names2=List(A, L, I, C, E, B, O, B, N, I, C, K)
  }

  def upper(s: String): String = {
    s.toUpperCase()
  }
}

11.2 集合元素的过滤-filter

基本的说明
filter :将符合要求的数据 ( 筛选 ) 放置到新的集合中
案例演示:
应用案例:将 val names = List("Alice", "Bob", "Nick") 集合中首字母为 'A' 的筛选到新的集合。
思考:如果这个使用传统的方式,如何完成 ?
object FilterDemo01 {
  def main(args: Array[String]): Unit = {

    //选出首字母为 A 的元素
    val names = List("Alice", "Bob", "Nick")
    val name2 = names.filter(startA)
    println(names)  //List(Alice, Bob, Nick)
    println(name2)  //List(Alice)
  }

  def startA(str:String):Boolean={
    str.startsWith("A")
  }
}

11.3 化简

11.3.1 看一个需求

val list = List(1, 20, 30, 4 ,5) , 求出 list 的和 .

11.3.2 化简的介绍

化简:将二元函数引用于集合中的函数 ,
上面的问题当然可以使用遍历 list 方法来解决,这里我们使用 scala 的化简方式来完成。
object ReduceDemo01 {
  def main(args: Array[String]): Unit = {
    //使用化简的方式来计算 list 集合的和
    val list=List(1,20,30,4,5)
    val res = list.reduceLeft(sum)
    //执行的流程分析
    //步骤 1 (1 + 20)
    //步骤 2 (1 + 20) + 30
    //步骤 3 ((1 + 20) + 30) + 4
    //步骤 4 (((1 + 20) + 30) + 4) + 5 = 60
    println("res="+res) //60
  }

  def sum(n1:Int,n2:Int):Int={
    n1+n2
  }
}

11.3.4 reduceLeft 的运行机制的说明

1) def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B
2) reduceLeft(f) 接收的函数需要的形式为 op: (B, A) => B): B
3) reduceleft(f) 的运行规则是 从左边开始执行将得到的结果返回给第一个参数
4) 然后继续和下一个元素运行,将得到的结果继续返回给第一个参数,继续 ..
: //((((1 + 2) + 3) + 4) + 5) = 15

11.3.5 化简的课堂练习

尚硅谷Scala (10-11)_第5张图片

object ReduceExercise01 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, 5)

    def minus(num1: Int, num2: Int): Int = {
      num1 - num2
    }

    // (((1-2) - 3) - 4) - 5 = -13
    println(list.reduceLeft(minus)) //-13
    // 1 - (2 - (3 -(4 - 5))) = 3
    println(list.reduceRight(minus)) //3
    // reduce 等价于 reduceLeft
    println(list.reduce(minus)) //-13

    println(list.max)
    println(list.min) //1
    println("minval=" + list.reduceLeft(min)) // 1
  }

  //求出最小值
  def min(n1: Int, n2: Int): Int = {
    if (n1 > n2) n2 else n1
  }
}

11.4 折叠

11.4.1 基本介绍

fold 函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到 list 中的所有元素被遍
历。
可以把 reduceLeft 看做简化版的 foldLeft
如何理解 :
        def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B =
                if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft")
                        else tail.foldLeft[B](head)(op)
大家可以看到 . reduceLeft 就是调用的 foldLeft[B](head) ,并且是默认从集合的 head 元素开始操作的。
相关函数: fold foldLeft foldRight ,可以参考 reduce 的相关方法理解

11.4.2 应用案例

object FoldDemo01 {
  def main(args: Array[String]): Unit = {
    val list =List(2,3,4,5)
    def minus(num1:Int,num2:Int):Int={
      num1-num2
    }

    //说明
    //1. 折叠的理解和化简的运行机制几乎一样. //理解 list.foldLeft(6)(minus) 理解成 list(6, 2, 3, 4,5) list.reduceLeft(minus)
    //步骤 (6-2)
    //步骤 ((6-2) - 3)
    //步骤 (((6-2) - 3) - 4)
    //步骤 ((((6-2) - 3) - 4)) - 5 = -8
    println(list.foldLeft(6)(minus))  //-8

    理解 list.foldRight(6)(minus) 理解成 list(2, 3, 4, 5,6) list.reduceRight(minus)
    // 步骤 (5 - 6)
    // 步骤 (4- (5 - 6))
    // 步骤 (3 -(4- (5 - 6)))
    // 步骤 2- (3 -(4- (5 - 6))) = 4
    println(list.foldRight(6)(minus)) //4
  }
}

11.4.3 foldLeft foldRight 缩写方法分别是:/::\

在2.13.11中不推荐使用,已过时,可以直接使用flodLeft或flodRight
object FlodDemo02 {
  def main(args: Array[String]): Unit = {
    val list4 = List(1, 9)
    def minus(num1: Int, num2: Int): Int = {
      num1 - num2
    }

    var i6=(1/:list4)(minus)  // =等价=> list4.foldLeft(1)(minus)
    println(i6) //-9
    var i7=(list4:\10)(minus)   // list4.foldRight(10)(minus)
    println(i7) //2
  }
}

11.5 扫描

11.5.1 基本介绍

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

11.5.2 应用实例

object ScanDemo01 {
  def main(args: Array[String]): Unit = {
    //普通函数
    def minus(num1:Int,num2:Int):Int={
      num1-num2
    }

    //5 (1,2,3,4,5) =>(5, 4, 2, -1, -5, -10) //Vector(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
    }
    //(1,2,3,4,5) 5 => (20,19,17,14, 10,5)
    val i9 = (1 to 5).scanRight(5)(add) //IndexedSeq[Int]
    println("i9=" + i9)
  }
}

11.6 集合综合应用案例

11.6.1 课堂练习 1

val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD" sentence 中各个字符,通过
foldLeft 存放到 一个 ArrayBuffer 中,目的:理解 flodLeft 的用法 . ArrayBufer('A','A','A'..)
object Exercise02 {
  def main(args: Array[String]): Unit = {
    val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
    val arrayBuffer = new ArrayBuffer[Char]()
    //理解折叠的第一个传入的 arrayBuffer 含义.
    sentence.foldLeft(arrayBuffer)(putArray)
    println("arrayBuffer="+arrayBuffer)
  }

  def putArray(arr: ArrayBuffer[Char], c: Char): ArrayBuffer[Char] = {
//将 c 放入到 arr 中
    arr.append(c)
    arr
  }
}

11.6.2 课堂练习 2

val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
使用映射集合,统计一句话中,各个字母出现的次数
提示: Map[Char, Int]()
看看 java 如何实现
String sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD";
Map charCountMap =
new HashMap();
char[] cs = sentence.toCharArray();
for ( char c : cs ) {
    if ( charCountMap.containsKey(c) ) {
    Integer count = charCountMap.get(c);
    charCountMap.put(c, count + 1);
} else {
    charCountMap.put(c, 1);
    }
}
System.out.println(charCountMap);
使用 scala flodLeft 折叠方式实现
object Exercise03 {
  def main(args: Array[String]): Unit = {
    val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
    val map2 = sentence.foldLeft(Map[Char, Int]())(charCount)
    println("map2="+map2)

    //使用可变的 map,效率更高. //1. 先创建一个可变 map,作为左折叠的第一个参数
    val map3 = mutable.Map[Char,Int]()
    sentence.foldLeft(map3)(charCount2)
    println("map3=" + map3)

  }

  //使用不可变Map实现
  def charCount(map:Map[Char,Int],char:Char):Map[Char,Int]={
    map+(char -> (map.getOrElse(char,0)+1))
  }

  //使用可变Map实现
  def charCount2(map:mutable.Map[Char,Int],char:Char):mutable.Map[Char,Int]={
    map+=(char -> (map.getOrElse(char,0)+1))
  }
}

11.6.3 课后练习 3-大数据中经典的 wordcount 案例

val lines = List("atguigu han hello ", "atguigu han aaa aaa aaa ccc ddd uuu")
使用映射集合, list 中,各个单词出现的次数,并按出现次数排序
val lines = List("atguigu han hello ", "atguigu han aaa aaa aaa ccc ddd uuu")

// 将每个单词拆分出来,并统计出现次数
val wordCountMap = lines
  .flatMap(_.split("\\s+"))
  .groupBy(identity)
  .mapValues(_.size)

// 按出现次数降序排序
val sortedWordCount = wordCountMap.toSeq.sortBy(-_._2)

// 打印结果
sortedWordCount.foreach { case (word, count) =>
  println(s"$word: $count")
}

11.7 扩展-拉链(合并)

11.7.1 基本介绍

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

11.7.2 应用实例

object ZipDemo01 {
  def main(args: Array[String]): Unit = {
    //拉链
    val list1=List(1,2,3)
    val list2=List(4,5,6)
    val list3=list1.zip(list2)
    println(list3)  //List((1,4), (2,5), (3,6))
  }
}


11.7.3 拉链的使用注意事项

1) 拉链的本质就是两个集合的合并操作,合并后每个元素是一个 对偶元组。
2) 操作的规则下图 :
尚硅谷Scala (10-11)_第6张图片

 

3) 如果两个集合个数不对应,会造 成数据丢失
4) 集合不限于 List, 也可以是其它集合比如 Array
5) 如果要取出合并后的各个对偶元组的数据,可以遍历
for(item<-list3){f
print(item._1 + " " + item._2) // 取出时,按照元组的方式取出即可

11.8 扩展-迭代器

11.8.1 基本说明

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

11.8.2 应用案例

object IteratorDemo01 {
  def main(args: Array[String]): Unit = {
    val iterator = List(1, 2, 3, 4, 5).iterator

    /*
    这里我们看看 iterator 的继承关系
    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()
*/

    println("---------------遍历方式1---------------")
    while(iterator.hasNext){
      println(iterator.next())
    }
    println("---------------遍历方式2---------------")
    for(enum<- iterator){
      println(enum) //上面执行过,里面没有内容
    }
  }
}

11.8.3 对代码小结

11.8.3 对代码小结
1) iterator 的构建实际是 AbstractIterator 的一个匿名子类,该子类提供了
/*
def iterator: Iterator[A] = new AbstractIterator[A] {
var these = self
def hasNext: Boolean = !these.isEmpty
def next(): A =
*/
2) AbstractIterator 子类提供了 hasNext next 等方法 .
3) 因此,我们可以使用 while 的方式,使用 hasNext next 方法变量

11.9 扩展-Stream

11.9.1 基本说明

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

11.9.2 创建 Stream 对象

案例 :
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)

11.9.3 流的应用案例

object StreamDemo01 {
  def main(args: Array[String]): Unit = {
    //创建Stream
    def numsForm(n:BigInt):Stream[BigInt] =n #::numsForm(n+1)
    val stream1=numsForm(1)
    println(stream1)  //Stream(1, )

    //取出第一个元素
    println("head="+stream1.head) //head=1
    println(stream1.tail) //Stream(2, )// 当对流执行 tail 操作时,就会生成一个新的数据.
    println(stream1)  //Stream(1, 2, )

    //看一个案例
    def multi(x:BigInt):BigInt={
      x*x
    }
    println(numsForm(5).map(multi)) //Stream(25, )
  }
}

11.10 扩展-视图 View

11.10.1 基本介绍

Stream 的懒加载特性,也可以对其他集合应用 view 方法来得到类似的效果 ,具有如下特点:
1) view 方法产出一个总是 被懒执行的集合
2) view 不会缓存数据,每次都要重新计算,比如遍历 View 时。

11.10.2 应用案例

请找到 1-100 中,数字倒序排列 和它本身相同的所有数。 (1 2, 11, 22, 33 ...)
object ViewDemo01 {
  def main(args: Array[String]): Unit = {
    def multiple(num:Int):Int={
      num
    }

    //如果这个数,逆序后和原来数相等,就返回 true,否则返回 false
    def eq(i:Int):Boolean={
      i.toString.equals(i.toString.reverse)
    }

    //说明:没有使用View,常规方式
    val viewSquares1=(1 to 100).filter(eq)
    println(viewSquares1) //Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99)

    //使用View完成,来完成这个问题,程序中,对集合进行 map,filter,reduce,fold...
    // 你并不希望立即执行,而是在使用到结果才执行,则可以使用 view 来进行优化.
    val viewSquares2=(1 to 100)
      .view
      .filter(eq)
    println(viewSquares2) //View()
    //遍历  懒加载,用的时候才会加载出来
    for(item <- viewSquares2){
      print(" "+item+" ") // 1  2  3  4  5  6  7  8  9  11  22  33  44  55  66  77  88  99
    }
  }
}

11.11 扩展-并行集合

11.11.1 基本介绍

1) Scala 为了充分使用多核 CPU ,提供了并行集合(有别于前面的串行集合),用于多核环境的
并行计算。
2) 主要用到的算法有:
Divide and conquer : 分治算法, Scala 通过 splitters( 分解器 ) combiners (组合器)等抽象层来实现,主要原理是将计算工作分解很多任务,分发给一些处理器去完成,并将它们处理结果合并返回 Work stealin 算法【学数学】,主要用于任务调度负载均衡( load-balancing ),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这样达到尽早干完的目的

11.11.2 应用案例

parallel(pærəlel 并行 )
打印 1~5
(1 to 5).foreach(println(_))
println()
(1 to 5).par.foreach(println(_))
查看并行集合中元素访问的线程
object ParDemo02 {
def main(args: Array[String]): Unit = {
val result1 = (0 to 100).map{case _ => Thread.currentThread.getName}.distinct
val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}.distinct
println(result1) //非并行
println("--------------------------------------------")
println(result2) //并行
}
}

你可能感兴趣的:(Scala,scala)