Scala集合

本文详细介绍了Scala的集合,包括可变与不可变集合,如Array、ArrayBuffer、List、Set、Map以及元组、队列等。讨论了数组的创建、转换,以及List、Set、Map的操作,并提及了并行集合的概念。


一.集合简介

1.Scala集合有三大类:所有集合都扩展自Iterable特质

  • Seq:序列——存放单值类型;有序,可重复
  • Set:集——存放单值类型;无序,不可重复
  • Map:映射——存放键值对;其中key无序,不可重复

下图显示了 scala.collection 中的所有集合,这些都是高级抽象类或特质,它们通常具有可变的和不变的实现

在这里插入图片描述

2.对于所有的集合类scala又同时提供可变与不可变的版本,分别位于以下这两个包:

  • scala.collection.immutable
  • scala.collection.mutable

1)Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改:类似于java中的String对象

2)可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象:类似于java中StringBuilder对象

建议:在操作集合的时候,不可变用符号,可变用方法

1.可变集合

下图显示了scala.collection.immutable 中的所有集合:

在这里插入图片描述
注意:

1)Set、Map是Java中也有的集合

2)Seq是Java没有的,我们发现List归属到了Seq,因此这里的List就和Java不是同一个概念了

3)String也是属于IndexedSeq(虚线:隐式转换)

4)Map体系有一个SortedMap,说明Scala的Map可以支持排序

5)IndexedSeq和LinearSeq的区别

  • a)IndexedSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位
  • b)LinearSeq是线性的,即有头尾的概念,这种数据结构一般是通过遍历来查找

6)我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Range

2.不可变集合

下图显示了scala.collection.mutable中的所有集合:

在这里插入图片描述
图中说明:
在这里插入图片描述


二.数组

Array的继承关系:

隐式转换
Iterable
Seq
IndexedSeq
ArrayBuffer
  • Seq:序列——存放单值类型;有序,可重复
  • IndexedSeq:通过索引来查找和定位

ArrayBuffer的继承关系:

Iterable
Seq
Buffer
IndexedSeq
ArrayBuffer

1.不可变数组:Array

不可变数组,所以在执行添加或者删除操作的时候,会创建新的数组对象。

有两种创建不可变数组的方式:new 和 apply

1)方式1:通过new Array[ ]( )

  • [Int]:是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any
  • (5):表示数组的大小,确定后就不可以变化
  • 默认赋值:数值类型——各类0;String——null
方式1:
val arr01 : Array[Int] = new Array[Int](5)
//1.赋值
arr01(0) = 10	//注意:区别于java用的是小括号
arr01.update(0, 1) //采用方法给数组赋值

//2.遍历数组
//(1)查看数组:用指定的字符串连接数组中的元素,形成一个新的字符串
println(arr01.mkString(","))    //10,1,0,0,0

//2)普通for循环:快捷输入  itr
for (i <- arr01) {
  println(i)
}

//3)迭代器.iterator
val iterator = arr01.iterator
while (iterator.hasNext){
  println(iterator.next())
}

//4)增强for循环:.foreach
//    arr01.foreach((i:Int)=>{println(i)})
//    arr01.foreach(println(_))
arr01.foreach(println)

//3.增加元素(由于创建的是不可变数组,增加元素,其实是产生新的数组)
val newArr1 = arr01.+:(30)
println(newArr1.mkString(","))  //30,10,1,0,0,0

//在Scala语言中,如果运算符方法中包含冒号,并且冒号在后,运算顺序为从右到左
//val newArr2: Array[Int] = arr01 +: 30  编译报错
val newArr3: Array[Int] = 30 +: arr01
println(newArr3.mkString(",")) //30,10,1,0,0,0


//同理冒号在左边:30添加到末尾
val newArr4 = arr01 :+ 30
println(newArr4.mkString(",")) //10,1,0,0,0,30

2)方式2:通过 Array(1, 2)

  • 在定义数组时,直接赋初始值
  • 使用apply方法创建数组对象
方式2:
val arr2 : Array[Int] = Array(1,2,3,4,5)

2.可变数组:ArrayBuffer

可变数组,在执行添加或者删除操作的时候,不会创建新的数组对象,直接在源数组上进行操作:

  • 包名:scala.collection.mutable.ArrayBuffer

两种方式:new 和 apply

//方式1:
val arr0: ArrayBuffer[Int] = ArrayBuffer(1,2,3)
//方式2:
val arr = new ArrayBuffer[Int]()
println(arr.length)  //0

//1.向数组中添加元素:append方法(),支持可变参数
//建议:在操作集合的时候,不可变用符号,可变用方法
//    arr(0) = 20  运行出错:数组中并没有元素
arr.append(10)
arr.insert(1,20)
//    arr.insert(5,20) 运行出错:数组长度没有到达索引5位置
println(arr.mkString(","))  //10,20
//    arr.update(0,10)

//2.访问数组中的元素  对数组中的元素进行修改
arr(0) = 20
arr.update(1,30)
println(arr.mkString(","))  //20,30

//3.注意:并不是数组是可变的,在操作数组对象的时候,就不能创建新的数组对象了
val newArr = arr :+ 40
println(newArr.mkString(","))  //20,30,40
println(arr.mkString(","))  //20,30

//4.删除数组中的元素
arr.append(40,50,60)
val num: Int = arr.remove(1)
println(num)

arr.remove(1,2)
println(arr.mkString(","))

3.不可变与可变数组的转换

  • arr1.toBuffer //不可变数组转可变数组,返回结果才是一个可变数组,arr1本身没有变化
  • arr2.toArray //可变数组转不可变数组,返回结果才是一个不可变数组,arr2本身没有变化
//可变数组转换为不可变数组
val arr2: ArrayBuffer[Int] = ArrayBuffer(1,2,3)
val newArr2: Array[Int] = arr2.toArray

//不可变数组转换为可变数组
val buffer: mutable.Buffer[Int] = newArr2.toBuffer

4.多维数组

val arr = Array.ofDim[Double](3,4)
//对二维数组进行遍历
for (i <- 0 until arr.length;j <- 0 until (arr(i).length)){
  println(arr(i)(j))
}

说明:二维数组中有三个一维数组,每个一维数组中有四个元素


三.Seq 列表:List

List继承关系:

Iterable
Seq
LinearSeq
List
  • Seq:序列——存放单值类型;有序,可重复
  • LinearSeq:线性的,即有头尾的概念,这种数据结构一般是通过遍历来查找

ListBuffer的继承关系:

Iterable
Seq
Buffer
ListBuffer

1.不可变 List

1)创建List集合:因为List是抽象的,只能通过apply方式创建
2)添加元素: +:、:+、
3)扁平化:三冒号
4)Nil 表示空集合
5)获取元素、遍历元素

object TestList {
  def main(args: Array[String]): Unit = {
    //1.创建List集合:因为List是抽象的,只能通过apply方式创建
    val list = List(1, 2, 3, 4, 5)

    //2.向集合中添加元素:不可变集合——用符号
    val newList1 = list.+:(20)  //20 +: list
    val newList2 = list.:+(20)  //list :+ 20
    println(newList1)  //List(20, 1, 2, 3, 4, 5)
    println(newList2)  //List(1, 2, 3, 4, 5, 20)

    //3.向集合中添加元素:双冒号、三冒号
    val list2 : List[Int] = List(1,2,3,4)
    val list3 : List[Int] = List(5,6)
    //双冒号:List整体作为元素添加
    val newList3: List[Any] = list2 :: list3
    println(newList3)  //List(List(1, 2, 3, 4), 5, 6)
    //三冒号:List扁平化处理后添加
    val newList4: List[Any] = list2 ::: list3
    println(newList4)  //List(1, 2, 3, 4, 5, 6)

    //4.在Scala语言中,List()===>Nil 表示空集合
    val newList5: List[Int] = 10 :: 20 :: 30 :: 40 :: Nil
    println(newList5)  //List(10, 20, 30, 40)

    //5.获取元素
    println(list(4))

    //6.遍历
    list.foreach(println)
  }
}

2.可变 ListBuffer

//0.需要导入包
import scala.collection.mutable.ListBuffer
object TestListBuffer {
  def main(args: Array[String]): Unit = {
    //1.创建可变集合对象
    // 方式1:new 方法
    val list0: ListBuffer[Int] = new ListBuffer[Int]()
    // 方式2:apply 方法
    val list: ListBuffer[Int] = ListBuffer(1,2,3)

    //2.向集合中添加元素  
    // 建议:可变用方法,不可变用符号
    list.append(10)
    list.insert(1,20)
    println(list)   //ListBuffer(1, 20, 2, 3, 10)

    //3.修改元素
    list(0) = 30
    list.update(0,30)
    println(list)  //ListBuffer(30, 20, 2, 3, 10)

    //4.删除
    list.remove(1,2)
    println(list)  //ListBuffer(30, 3, 10)

    val list2: ListBuffer[Int] = ListBuffer(10,20,30,40)
    // 1) - 方法是对原ListBuffer的克隆后进行元素的删除操作,返回一个新的值,原值未变
    val anotherList = list2.-(20)
    println(list2)  //ListBuffer(10, 20, 30, 40)
    println(anotherList)  //ListBuffer(10, 30, 40)
    // 2) -= 方法是对原ListBuffer直接进行操作
    list2.-=(30)
    println(list2)  //ListBuffer(10, 20, 40)
  }
}

四.Set 集合

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

注意区别:

  • Seq:序列——存放单值类型;有序,可重复
  • Set:集——存放单值类型;无序,不可重复
  • Map:映射——存放键值对;其中key无序,不可重复

1.不可变Set

特点:不可变、数据无序、不可重复

//(1)Set默认是不可变集合,数据无序,不可重复
val set: Set[Int] = Set(1, 2, 3, 4, 5, 3, 4)
println(set)  //Set(5, 1, 2, 3, 4)

//2)添加元素
val set1 = set.+(20)
println(set1)  //Set(5, 20, 1, 2, 3, 4)

//3)遍历
set.foreach(println)

2.可变 mutable.Set

需要引用 scala.collection.mutable.Set

//(1)创建可变集合
val set2 = mutable.Set(1, 2, 3, 4, 5, 3, 4)

//(2)集合添加元素
set2.add(20)
set2 += 30
println(set2)  //Set(30, 1, 5, 2, 20, 3, 4)

//(3)删除元素
set2-=(5)
set2.remove(4)
println(set2)  //Set(30, 1, 2, 20, 3)

//(4)向集合中添加元素,返回一个新的Set
val ints = set2.+(9)   //override def + (elem: A): This = clone() += elem
println(ints)  //Set(30, 9, 1, 2, 20, 3)
println(set2)  //Set(30, 1, 2, 20, 3)

//5)遍历、打印
set2.foreach(println)
println(set2.mkString(","))  //30,1,2,20,3

五.Map集合

Map:映射——存放键值对;其中key无序,不可重复。

Scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射。

1.不可变 Map

//1.创建不可变map集合    底层就是HashMap
val map: Map[String, Int] = Map("a" -> 1, "b" -> 2, "c" -> 3)

//2.获取keys\values\value
for (key <- map.keys ) {
  //在Scala语言中,为了避免空指针异常,如果获取的内容,有可能为空,则可以用Option表示
  //当Map中有该key,则get(key)返回 Some(value),否则返回None
  print(key + "=" + map.get(key) + "\t")  //a=Some(1) b=Some(2) c=Some(3)
}
println(map.get("d"))  //None
println(map.getOrElse("d", 0))  //0

for (value <- map.values ) {
  print(value + "\t")  //1	2	 3
}

//3.添加元素,形成一个新的Map
val map1 = map.+("d" -> 5)
println(map.mkString(","))  //a -> 1,b -> 2,c -> 3
println(map1.mkString(","))  //a -> 1,b -> 2,c -> 3,d -> 5

在Scala语言中,为了避免空指针异常,如果获取的内容,有可能为空,则可以用Option表示。

当调用map.get方法的时候,返回的Option类型数据,Option有两个子类型,一个Some,另一个None:

  • 当Map中有该key,则get(key)返回 Some(value)
  • 否则返回None
  • 如果想直接取值,而又不想取到None则可以使用:map.getOrElse(key, 默认值)

2.可变 mutable.Map

//可变map集合
val map: mutable.Map[String, Int] = mutable.Map("a"->1,"b"->2,"c"->3)

//建议:可变用方法,不可变用符号
//添加
map.put("d",4)
map.+=("e"->4)

//删除元素
map.remove("d")
map.-=("b")   

//修改map集合中元素
map.update("a",20)
map("a") = 30
map.foreach(println)

六.元组 Tuple

元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。

注意:元组中最大只能有22个元素

object TestTuple {
  def main(args: Array[String]): Unit = {
    //(1)声明元组的方式:(元素1,元素2,元素3)
    //数据类型:Int\String\(参数类型)=>返回值类型\ =>(返回值类型) \ (类型...)
    val tuple: (Int, String, Boolean) = (5, "zhangsan", true)

    //(2)访问元组
    //(2.1)通过元素的顺序进行访问,调用方式:_顺序号
    println(tuple._1)
    println(tuple._2)
    println(tuple._3)

    //(2.2)通过索引访问数据
    println(tuple.productElement(0))

    //(2.3)通过迭代器访问数据
    for (elem <- tuple.productIterator) {
      println(elem)
    }

    //(3)Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶
    val map: Map[String, Int]  = Map("a"->1, "b"->2, "c"->3)
    val map1: Map[String, Int]  = Map(("a",1), ("b",2), ("c",3))

    map.foreach(tuple=>{println(tuple._1 + "=" + tuple._2)})
  }
}

七.队列 Queue

Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue

Queue继承关系:

Iterable
Seq
LinearSeq
Queue
val que = new mutable.Queue[String]()

que.enqueue("a", "b", "c")
println(que)  //Queue(a, b, c)

println(que.dequeue())
println(que.dequeue())
println(que.dequeue())
println(que)  //Queue()

八.集合常用函数

1.基本属性和常用操作

(1)获取集合长度
	length
(2)获取集合大小
	size
(3)循环遍历
	foreach
(4)迭代器
	iterator
(5)生成字符串
	mkString
(6)是否包含
	contains

2.衍生集合

(1)获取集合的头
	head
(2)获取集合的尾(不是头的就是尾)
	tail
(3)集合最后一个数据
	last
(4)集合初始数据(不包含最后一个)
	init
(5)反转
	reverse
(6)取前(后)n个元素:从0到n——n>=1
	take|takeRight
(7)去掉前(后)n个元素
	drop|dropRight
(8)并集
	union
(9)交集
	intersect
(10)差集
	diff
(11)拉链
	zip
	注:如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用
(12)滑窗:x滑窗大小,y每次滑几步
	sliding(x, y)

3.集合计算初级函数

(1)求和
	sum
(2)求乘积
	product
(3)最大值
	max
(4)最小值
	min
(5)排序
	sorded|sortBy|sortWith

排序:

  • 1)sorted
    • 对一个集合进行自然排序(升序),通过传递隐式的Ordering
  • 2)sortBy
    • 对一个属性或多个属性进行排序,通过它的类型。
  • 3)sortWith
    • 基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑

Seq是有序集合,它才有排序方法,Map、Set是无序集合,需要转换为Seq才能进行下一步排序。

//排序
val list: List[Int] = List(1, 5, -3, 4, 2, -7, 6)
println(list.sorted)  //升序
// 1)按照元素大小排序
//如果函数参数和函数体是一致的情况,那么不能简化
println(list.sortBy(x => x))  //升序

// 2)按照元素的绝对值大小排序
println(list.sortBy(x => x.abs))  //绝对值 + 升序

// 3)按元素大小升序排序
println(list.sortWith((x, y) => x < y))

// 4)按元素大小降序排序
println(list.sortWith((x, y) => x > y))

注意:如果函数参数和函数体是一致的情况,那么不能简化

  • println(list.sortBy(x => x))

4.集合计算高级函数

(1)过滤 filter(函数:指定过滤条件) 
	 遍历一个集合并从中获取满足指定条件的元素组成一个新的集合

(2)转换/映射  map
			
(3)扁平化 flatten   :::
	将集合中元素由整体转换为个体的过程

(4)扁平映射  flatMap  
	先映射再进行扁平化处理

(5)分组	gruopBy
	按照一定的分组规则,将集合中的元素放到不同的组中

(6)简化|规约  reduce   
	>对集合内部元素之间进行聚合 
	>reduce   聚合的数据类型一致
              底层调用的是reduceLedft
              函数参数类型必须一致
	>reduceLeft|reduceRight 	聚合的数据类型可以不一致

(7)折叠  fold 	
	>对外部元素和集合内部元素之间进行聚合
	>fold 	聚合的数据类型一致
	>foldLeft|foldRight		聚合的数据类型可以不一致
object TestFunction {
  def main(args: Array[String]): Unit = {
    val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

    //(1)过滤 遍历一个集合并从中获取满足指定条件的元素组成一个新的集合
    //例:对List集合进行遍历,将偶数取出,放到新的集合中去
    // 初始版: list.filter((i: Int) => { i % 2 == 0 })
    println(list.filter(_ % 2 == 0))  // List(2, 4, 6, 8)

    //(2)转化/映射(map) 将集合中的每一个元素映射到某一个函数
    //    list.map((i:Int) => {i*2})
    println(list.map(_ * 2))  // List(2, 4, 6, 8, 10, 12, 14, 16, 18)

    //(3)扁平化   :::
    val nestedList : List[List[Int]]  = List(List(1,2,3),List(4,5,6),List(7,8,9))
    println(nestedList.flatten)   //List(1, 2, 3, 4, 5, 6, 7, 8, 9)

    //(4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作  集合中的每个元素的子元素映射到某个函数并返回新集合
    val strings: List[String] = List("hello world","hello China","hello beauty")
    //    strings.flatMap((s:String)=>s.split(" "))
    println(strings.flatMap(_.split(" ")))  //List(hello, world, hello, China, hello, beauty)

    //等价于
    //a)先对集合中的元素进行转换   字符串---->数组
    val splitList: List[Array[String]] = strings.map(_.split(" "))
    for (arr <- splitList ) {
      println(arr.mkString(","))
    }
    //b)将集合中的元素由整体 --->个体
    val flatList: List[String] = splitList.flatten
    println(flatList)

    //(5)分组(groupBy) 按照指定的规则对集合的元素进行分组
    //按照是否为偶数分组  : list.groupBy((i:Int)=>i%2)
    val map: Map[Int, List[Int]] = list.groupBy(_ % 2)
    println(map)  //Map(1 -> List(1, 3, 5, 7, 9), 0 -> List(2, 4, 6, 8))

    val nameList: List[String] = List("liuyi","zhangsan","zhaoliu","lisi")
    //按照首字母分组
    val map2: Map[Char, List[String]] = nameList.groupBy(_.charAt(0))
    println(map2)  //Map(z -> List(zhangsan, zhaoliu), l -> List(liuyi, lisi))

    //6)规约:
    //reduce 函数参数类型必须一致;  底层调用的是reduceLedft
    println(list.reduce((a: Int, b: Int) => {
      a + b
    }))  //45

    //reduceLeft    函数参数类型可以不一致
    // reduceLeft[B >: A]
    println(list.reduceLeft(_-_))  //-43

    //reduceRight   函数参数类型可以不一致
    println(list.reduceRight(_-_))  //5

    //7)折叠   集合外元素和集合内部元素进行聚合
    val res: Int = list.fold(10)(_+_)
    println(res)  //55

    println(list.foldLeft(10)(_ + _))  //55

    println(list.foldRight(11)(_ - _))  //-6
  }
}

具体运行原则:

  • reduceLeft 和 foldLeft:
override /*TraversableLike*/
def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B =
  if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft")
  else tail.foldLeft[B](head)(op)


override /*TraversableLike*/
def foldLeft[B](z: B)(@deprecatedName('f) op: (B, A) => B): B = {
  var acc = z
  var these = this
  while (!these.isEmpty) {
    acc = op(acc, these.head)
    these = these.tail
  }
  acc
}
  • reduceRight:
override /*IterableLike*/
def reduceRight[B >: A](op: (A, B) => B): B =
  if (isEmpty) throw new UnsupportedOperationException("Nil.reduceRight")
  else if (tail.isEmpty) head
  else op(head, tail.reduceRight(op))
  • foldRight:
override def foldRight[B](z: B)(op: (A, B) => B): B =
  reverse.foldLeft(z)((right, left) => op(left, right))

5.案例

案例1:两个map集合之间的合并

def main(args: Array[String]): Unit = {
    val map1: mutable.Map[String, Int] = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
    val map2: mutable.Map[String, Int] = mutable.Map("a"->4, "b"->5, "d"->6)

    val result: mutable.Map[String, Int] = map1.foldLeft(map2) {
      //map表示map2,kv表示map1中的每一个元素
      (map, kv) => {
        val k: String = kv._1
        val v: Int = kv._2
        //根据map1中元素的key,到map2中获取value
        map(k) = map.getOrElse(k, 0) + v
        map
      }
    }
    println(result)  //Map(b -> 7, d -> 6, a -> 5, c -> 3)
}

案例2:WordCount

需求:单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果

def main(args: Array[String]): Unit = {
  // 单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
  val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello")

  // 1) 将每一个字符串转换成一个一个单词
  val wordList = stringList.flatMap(a => a.split(" "))
  println(wordList)  //List(Hello, Scala, Hbase, kafka, Hello, Scala, Hbase, Hello, Scala, Hello)

  // 2) 将相同的单词放置在一起
  val wordToMap = wordList.groupBy(i=>i)
  println(wordToMap)  //Map(Hello -> List(Hello, Hello, Hello, Hello),......

  // 3) 对相同的单词进行计数
  val wordAndCount: Map[String, Int] = wordToMap.map { kv => (kv._1, kv._2.size) }

  // 4) 对计数完成后的结果进行排序(降序)
  val sortList: List[(String, Int)] = wordAndCount.toList.sortWith {
    (tuple1, tuple2) => tuple1._2 > tuple2._2
  }

  // 5) 对排序后的结果取前3名
  val result: List[(String, Int)] = sortList.take(3)
  println(result)  //List((Hello,4), (Scala,3), (Hbase,2))
}

九.并行集合

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

案例实操

def main(args: Array[String]): Unit = {
    val result1 = (0 to 100).map{case _ => Thread.currentThread.getName}
    val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}

    println(result1)  //Vector(main, main, main,...
    println(result2)  //ParVector(ForkJoinPool-1-worker-5, ForkJoinPool-1-worker-5,...
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值