10.1数据结构特点
1)Scala同时支持不可变集合和可变集合
2)两个主要的包:
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
3)Scala默认采用不可变集合,对于几乎所有的集合类,Scala都同时提供了可变(mutable)和不可变(immutable)的版本
4)Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质,在Scala中集合有可变(mutable)和不可变(immutable)两种类型。
10.1.1可变集合和不可变集合举例
1)不可变集合:scala不可变集合,就是这个集合本身不能动态变化。(类似java的数组,是不可以动态增长的)
2)可变集合:可变集合,就是这个集合本身可以动态变化的。(比如:ArrayList , 是可以动态增长的)
3)代码
import java.util.ArrayList;
public class MutableImmutableDemo {
public static void main(String[] args) {
//不可变集合类似java的数组
int[] nums = new int[3];
nums[2] = 11; //?
//nums[3] = 90; //,即不能动态的增长
// String[] names = {"bj", "sh"};
// System.out.println(nums + " " + names);
//
可变集合举例
ArrayList al = new ArrayList<String>();
al.add("zs");
al.add("zs2");
System.out.println(al + " " + al.hashCode()); //地址1
al.add("zs3");
System.out.println(al + " " + al.hashCode()); //地址2
}
}
10.2不可变集合继承层次一览图
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 是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找,它的价值在于应用到一些具体的应用场景 (电商网站, 大数据推荐系统 :最近浏览的10个商品)
10.3可变集合体现一览图
10.4数组-定长数组(声明泛型)
10.4.1第一种方式定义数组
这里的数组等同于Java中的数组,中括号的类型就是数组的类型
val arr1 = new Array[Int](10)
//赋值,集合元素采用小括号访问
arr1(1) = 7
10.4.2代码演示
object ArrayDemo {
def main(args: Array[String]): Unit = {
val arr01 = new Array[Int](4) // //表示我创建一个数组,存放Int,大小4
println(arr01.length) // 4
println("arr01(0)=" + arr01(0)) //访问指定下标的元素
for (i <- arr01) { //遍历
println(i)
}
println("--------------------")
arr01(3) = 10 //修改
for (i <- arr01) {
println(i)
}
}
}
10.4.3第二种方式定义数组
在定义数组时,直接赋值//使用apply方法创建数组对象
val arr1 = Array(1, 2,"hello")
代码:
var arr02 = Array(1, 3, "xxx")
for (i <- arr02) {
println(i)
}
10.5数组-变长数组(声明泛型)
基本使用和应用案例
//定义/声明
val arr2 = ArrayBuffer[Int]()
//追加值/元素
arr2.append(7)
//重新赋值
arr2(0) = 7
学习集合的流程(c[创建]
[查看元素,遍历]u[修改]d[删除])
案例演示+反编译
代码
import scala.collection.mutable.ArrayBuffer
object ArrayBufferDemo {
def main(args: Array[String]): Unit = {
//创建了一个 ArrayBuffer[Any] => 可存放任何对象
val arr01 = ArrayBuffer[Any](3, 2, 5)
println("arr01(1)=" + arr01(1))//可以下标访问IndexedSeq
for (i <- arr01) {
println(i)
}
println(arr01.length) //3
println("arr01.hash=" + arr01.hashCode())
//追加元素 ,append支持可变参数
arr01.append(90.0,13)
println("arr01.hash=" + arr01.hashCode())
arr01(1) = 89 //修改
println("--------------------------")
for (i <- arr01) {
println(i)
}
//
// //删除
arr01.remove(0) //表示将第一个元素删除
println("--------------------------")
for (i <- arr01) {
println(i)
}
println("最新的长度=" + arr01.length)
}
}
10.5.1变长数组的分析
1)ArrayBuffer是变长数组,类似java的ArrayList
2)val arr2 = ArrayBufferInt 也是使用的apply方法构建对象
3)def append(elems: A*) { appendAll(elems) } 接收的是可变参数.
4)每append一次,arr在底层会重新分配空间,进行扩容,arr2的内存地址会发生变化,也就成为新的ArrayBuffer
10.5.2定长数组与变长数组的转换
1)arr1.toBuffer //定长数组转可变数组
2)arr2.toArray //可变数组转定长数组
说明:
arr2.toArray 返回结果才是一个定长数组, arr2本身没有变化
arr1.toBuffer返回结果才是一个可变数组, arr1本身没有变化
代码
import scala.collection.mutable.ArrayBuffer
object Array2ArrayBuffer {
def main(args: Array[String]): Unit = {
val arr2 = ArrayBuffer[Int]()
// 追加值
arr2.append(1, 2, 3)
println(arr2)
//将arrayBuffer -> Array
val newArr = arr2.toArray
//如果希望将一个数组Array的内容输出,可以简单的使用mkString
println(newArr.mkString(" "))
val newArr2 = newArr.toBuffer
newArr2.append(123)
println(newArr2)
//案例演示+说明
}
}
10.6数组-多维数组
说明
//定义
val arr = Array.ofDim[Double](3,4)
说明:
arr 是一个二维数组
有三个元素[一维数组]
每个一维数组存放4个值
//赋值
arr(1)(1) = 11.11
代码
object MuArray {
def main(args: Array[String]): Unit = {
//array1是一个二维数组
val array1 = Array.ofDim[Int](3, 4)
array1(1)(1) = 9 //给(1)(1)
//item 是一个一维数组
for (item <- array1) {
//继续变量item一维数组
for (item2 <- item) {
print(item2 + "\t")
}
println()
}
//传统方法
for (i <- 0 to array1.length -1 ){
for (j <- 0 to array1(i).length -1) {
printf("array1[%d][%d]=%d\t",i,j,array1(i)(j))
}
println()
}
var arr2 = Array(Array(1,2), Array(4,5,6), Array("hello","北京"))
println("===============")
for (i <- 0 to arr2.length -1 ){
for (j <- 0 to arr2(i).length -1) {
printf("array1[%d][%d]=%s\t",i,j,arr2(i)(j))
}
println()
}
}
}
10.7数组-Scala数组与Java的List的互转
10.7.1Scala数组转Java的List
在项目开发中,有时我们需要将Scala数组转成Java数组,看下面案例:
import scala.collection.mutable.ArrayBuffer
object ArrayBuffer2JavaList {
def main(args: Array[String]): Unit = {
// Scala集合和Java集合互相转换
val arr = ArrayBuffer("1", "2", "3")
import scala.collection.JavaConversions.bufferAsJavaList
//即这里的bufferAsJavaList是一个隐式函数
/*
implicit def bufferAsJavaList[A](b : scala.collection.mutable.Buffer[A]) : java.util.List[
*/
val javaArr = new ProcessBuilder(arr) //为什么可以这样使用?
val arrList = javaArr.command()
println(arrList) //输出 [1, 2, 3]
}
}
10.7.2Java的List转scala的Buffer
//说明:asScalaBuffer 是一个隐式转换
/*
implicit def asScalaBuffer[A](l : java.util.List[A]) : scala.collection.mutable.Buffer[A]
*/
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable
// java.util.List ==> Buffer
val scalaArr: mutable.Buffer[String] = arrList
scalaArr.append("jack")
println(scalaArr)
10.8元组Tuple-元组的基本使用
10.8.1基本介绍
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。
说的简单点,就是将多个无关的数据封装为一个整体,称为元组, 最大的特点灵活,对数据没有过多的约束。 //需求-> 高效率
注意:元组中最大只能有22个元素
10.8.2元组的创建
//说明
//1. tuple 的类型是 Tuple3
//2. 根据元素个数不同,对应的类型是 Tuple1----Tuple22
//3. Tuple是一个整体,使用迭代器进行遍历
//4. 访问Tuple的元素的方式是 tuple._1 .... tuple._3
//5. 元组是scala推荐使用,在java没有
/*
final case class Tuple3[+T1, +T2, +T3](_1: T1, _2: T2, _3: T3)
extends Product3[T1, T2, T3]
{
override def toString() = "(" + _1 + "," + _2 + "," + _3 + ")"
}
*/
//创建元组
val tuple = (1, 2, "hello")
10.9元组Tuple-元组数据的访问
//访问元组
//1. 使用 _顺序号
println(tuple._3) // "hello"
//2. 使用
println(tuple.productElement(2)) //下标是从0开始计算
10.10元组的遍历
Tuple是一个整体,遍历需要调其迭代器
//遍历元组[通过迭代器来遍历]
for (i <- tuple.productIterator) {
println("i=" + i)
}
10.11列表 List-创建List
10.11.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.11.2创建List的应用案例
object ListDemo01 {
def main(args: Array[String]): Unit = {
//集合在scala.collection.mutable(immutalbe)._ 为什么不引人包也能使用List
//原因: package object scala 包中声明了
//val List = scala.collection.immutable.List
//val Nil = scala.collection.immutable.Nil
val list01 = List(1, 2, 3) //创建时,直接分配元素 apply
println(list01)
val list02 = Nil //空集合
println(list02)
}
}
10.11.3创建List的应用案例小结
1)List默认为不可变的集合
2)List 在 scala包对象声明的,因此不需要引入其它包也可以使用
3)val List = scala.collection.immutable.List
4)List 中可以放任何数据类型,比如 arr1的类型为 List[Any]
如果希望得到一个空列表,可以使用Nil对象, 在 scala包对象声明的,因此不需要引入其它包也可以使用
10.12列表 List-访问List元素
val value1 = list1(1) // 1是索引,表示取出第2个元素.
println(value1)
10.13列表 List-元素的追加
10.13.1基本介绍
向列表中增加元素, 会返回新的列表/集合对象。注意:Scala中List元素的追加形式非常独特,和Java不一样。
10.13.2方式1-在列表的最后增加数据
10.13.3方式2-在列表的最前面增加数据
案例演示
//定义了List
var list1 = List(1, 2, 3, "abc")
// :+运算符表示在列表的最后增加数据
// 说明 1. :+ 符号 : 前是集合 + 后是元素
val list2 = list1 :+ 4
println(list1) //list1没有变化
println(list2) //新的列表结果是 [1, 2, 3, "abc", 4]
//给list向前面追加
//说明 1. +: 符号 : 后是集合 + 前是元素
val list3 = 100 +: list1
println("list3=" + list3)
10.13.4方式3-在列表的最后增加数据
说明:
1)符号::表示向集合中 新建集合添加元素。
2)运算时,集合对象一定要放置在最右边,
3)运算规则,从右向左。
4)::: 运算符是将集合中的每一个元素加入到空集合中去, ::: 左右两边需要时集合.
应用案例:
// :: 使用
val list4 = List(1, 2, 3, "abc")
// 操作步骤
// 1. ()
// 2. (List(1, 2, 3, "abc"))
// 3. (6, List(1, 2, 3, "abc"))
// 4. (5, 6, List(1, 2, 3, "abc"))
// 5. (4, 5, 6, List(1, 2, 3, "abc"))
val list5 = 4 :: 5 :: 6 :: list4 :: Nil
println("list5=" + list5)
// ::: 的使用
//下面等价 4 :: 5 :: 6 :: list1
var list6 = List(1, 2, 3, "abc")
// 步骤
// 1. ()
// 2. (1, 2, 3, "abc")
// 3. (6, 1, 2, 3, "abc")
// 4. (5, 6, 1, 2, 3, "abc")
// 5. (4, 5, 6, 1, 2, 3, "abc")
val list7 = 4 :: 5 :: 6 :: list6 ::: Nil
println("list7=" + list7)
10.14列表 ListBuffer
ListBuffer:ListBuffer是可变的list集合,可以添加,删除元素,ListBuffer属于序
列 //追一下继承关系即可 Seq var listBuffer = ListBuffer(1,2)
import scala.collection.mutable.ListBuffer
object ListBufferDemo {
def main(args: Array[String]): Unit = {
val lst0 = ListBuffer[Int](1, 2, 3)
println("lst0(2)=" + lst0(2)) //访问指定元素
for (item <- lst0) { //遍历
println("item=" + item)
}
val lst1 = new ListBuffer[Int]
lst1 += 4 //lst1(4)
lst1.append(5) //lst1(4,5)
//++ 表示的是加入的是集合中的各个元素
lst0 ++= lst1 //lst0(1,2,3,4,5)
val lst2 = lst0 ++ lst1 //lst2(1,2,3,4,5,4,5)
val lst3 = lst0 :+ 5 // 单独的加入元素 (1,2,3,4,5,5)
println("lst0=" + lst0)
println("lst1=" + lst1)
println("lst2=" + lst2)
println("lst3=" + lst3)
println("=====删除=======")
println("lst1=" + lst1)
lst1(0) = 888
lst1.remove(1) //使用remove进行删除,(1)表示第二个元素
for (item <- lst1) {
println("item=" + item)
}
}
}
10.15队列 Queue-基本介绍
10.15.1队列的说明
1)队列是一个有序列表,在底层可以用数组或是链表来实现。
2)其输入和输出要遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
3)在Scala中,由设计者直接给我们提供队列类型使用。
4)在scala中, 有 scala.collection.mutable.Queue 和 scala.collection.immutable.Queue , 一般来说,我们在开发中通常使用可变集合中的队列
10.15.2队列 Queue-队列的创建
//说明: 这里的Int是泛型,表示q1队列只能存放Int类型
//如果希望q1可以存放其它类型,则使用 Any 即可。
val q1 = new mutable.Queue[Int]
println(q1)
10.16队列 Queue-队列元素的追加数据
10.16.1向队列中追加单个元素和List
//追加单个元素
q1 += 1
q1 += 2
println(q1)
q1 ++= List(4, 5, 6)
println(q1)
//如果希望加入一个List或者其他的类型,则需要将Queue的泛型指定为[Any]
//q1 += List(8,9)
println(q1)
10.17队列 Queue-删除和加入队列元素
说明:按照进入队列的顺序删除元素(队列先进先出)
代码;
//dequeue的操作,是出队列(即将队列的头元素取出)
val ele = q1.dequeue()
println("ele=" + ele) //1
println("q1=" + q1) // (2,4,5,6)
//enqueue的操作,表示给队列尾部加入数据
q1.enqueue(10,20,30)
println("q1=" + q1) // (2,4,5,6,10,20,30)
//head
println("q1.head=" + q1.head) // 2
//last
println("q1.last=" + q1.last) // 30
//tail的用法
println(q1.tail.tail) //(5,6,10,20,30)
println("q1=" + q1) // (2,4,5,6,10,20,30)
10.18返回队列中的元素
10.19映射 Map-基本介绍
10.19.1Java中的Map回顾
HashMap 是一个散列表(数组+链表),它存储的内容是键值对(key-value)映射,Java中的HashMap是无序的,key不能重复。案例演示:
public class TestJavaMap {
public static void main(String[] args) {
HashMap<String,Integer> 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.2Scala中的Map介绍
1)Scala中的Map 和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射,Scala中不可变的Map是有序的,可变的Map是无序的。
2)Scala中,有可变Map (scala.collection.mutable.Map) 和 不可变Map(scala.collection.immutable.Map)
10.20映射 Map-构建Map
10.20.1方式1-构造不可变映射
Scala中的不可变Map是有序,构建Map中的元素底层是Tuple2类型。
案例
val map1 = Map(“Alice” -> 10, “Bob” -> 20, “Kotlin” -> “北京”)
小结
1.从输出的结果看到,输出顺序和声明顺序一致
2.构建Map集合中,集合中的元素其实是Tuple2类型
3.默认情况下(即没有引入其它包的情况下),Map是不可变map
4.为什么说Map中的元素是Tuple2 类型 [反编译或看对应的apply]
10.20.2方式2-构造可变映射
//需要指定可变Map的包
val map2 = scala.collection.mutable.Map(“Alice” -> 10, “Bob” -> 20, “Kotlin” -> 30)
说明
1.从输出的结果看到,输出顺序和声明顺序不一致
10.20.3方式3-创建空的映射
val map3 = new scala.collection.mutable.HashMap[String, Int]
println(map3)
10.20.4方式4-对偶元组
即创建包含键值对的二元组, 和第一种方式等价,只是形式上不同而已。
对偶元组 就是只含有两个数据的元组。
val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30) )
println("map4=" + map4)
println(map4("A"))
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
10.21.2方式2-使用contains方法检查是否存在key
// 返回Boolean
// 1.如果key存在,则返回true
// 2.如果key不存在,则返回false
map4.contains("B")
说明:
使用containts先判断在取值,可以防止异常,并加入相应的处理逻辑
val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30.9) )
if( map4.contains("B") ) {
println("key存在 值= " + map4("B"))
} else {
println("key不存在")
}
10.21.3方式3-使用map.get(key).get取值
通过 映射.get(键) 这样的调用返回一个Option对象,要么是Some,要么是None
var map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
println(map4.get("A")) //Some
println(map4.get("A").get) //得到Some在取出
说明和小结:
1)map.get方法会将数据进行包
2)如果 map.get(key) key存在返回some,如果key不存在,则返回None
3)如果 map.get(key).get key存在,返回key对应的值,否则,抛出异常 java.util.NoSuchElementException: None.get
10.21.4方式4-使用map4.getOrElse()取值
getOrElse 方法 : def getOrElse[V1 >: V](key: K, default: => V1)
说明:
如果key存在,返回key对应的值。
如果key不存在,返回默认值。在java中底层有很多类似的操作。
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
println(map4.getOrElse("A","默认"))
10.21.5如何选择取值方式建议
1)如果我们确定key是存在的,应该使用map(“key”) ,速度快.
2)如果我们不确定key是否存在, 而且在不存在时,有业务逻辑处理就是用map.contains() 配合 map(“key”)
3)如果只是简单的希望返回一个值,就使用getOrElse()
10.22映射 Map-对map修改、添加和删除
10.22.1更新map的元素
案例:
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
map4("A") = 20 //修改和增加
println(map4)
说明:
1)map 是可变的,才能修改,否则报错
2)如果key存在:则修改对应的值,key不存在,等价于添加一个key-val
10.22.2添加map元素
10.22.3删除map元素
提示:map4.remove(key)
10.23映射 Map-对map遍历
map的遍历支持多种形式:
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是Tuple2
说明
1.每遍历一次,返回的元素是Tuple2
2.取出的时候,可以按照元组的方式来取
10.24集 Set-基本介绍
10.24.1集是不重复元素的结合。集不保留顺序,默认是以哈希集实现
10.24.2Java中Set的回顾
java中,HashSet是实现Set接口的一个实体类,数据是以哈希表的形式存放的,里面的不能包含重复数据。Set接口是一种不包含重复元素的 collection,HashSet中的数据也是没有顺序的。
public class JavaSet {
public static void main(String[] args) {
HashSet hs = new HashSet<String>();
hs.add("jack");
hs.add("tom");
hs.add("jack");
hs.add("jack2");
System.out.println(hs);
}
}
10.24.3Scala中Set的说明
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包
10.24.4Scala的可变Set和不可变Set的举例
import scala.collection.mutable
object ScalaSet01 {
def main(args: Array[String]): Unit = {
val set01 = Set(1,2,4,"abc")
println(set01)
val set02 = mutable.Set(1,2,4,"abc")
println(set02)
}
}
10.25集 Set-可变集合的元素添加和删除
10.25.1可变集合的元素删除
val set02 = mutable.Set(1,2,4,"abc")
set02 -= 2 // 操作符形式
set02.-=(4) // ok
set02.remove("abc") // 方法的形式,scala的Set可以直接删除值
println(set02)
说明:说明:如果删除的对象不存在,则不生效,也不会报错
10.25.2set集合的遍历
val set02 = mutable.Set(1, 2, 4, "abc")
for(x <- set02) {
println(x)
}