【大数据】初识Scala 3

本文涉及的产品
.cn 域名,1个 12个月
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
简介: 【大数据】初识Scala

代器

迭代器(Iterator)是一种用于遍历集合中元素的工具。它提供了一种方法来访问集合中的元素,而不需要暴露集合的内部结构。在 Scala 中,你可以使用 iterator 方法来获取一个集合的迭代器。

object Main {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3)
    val iterator = list.iterator
    // 1. 使用 hasNext 方法来检查迭代器中是否还有元素
    val hasMoreElements = iterator.hasNext
    println(s"Has more elements: $hasMoreElements")
    // 2. 使用 next 方法来获取迭代器中的下一个元素
    val nextElement = iterator.next()
    println(s"Next element: $nextElement")
    // 注意:上面的代码已经将迭代器移动到了第二个元素,因此下面的代码将从第二个元素开始执行
    // 3. 使用 size 方法来获取迭代器中元素的个数
    val size = iterator.size
    println(s"Size: $size")
    val size1 = iterator.size
    println(s"Size1: $size1")
    // 注意:上面的代码已经将迭代器移动到了末尾,因此下面的代码将不再有效
    // 4. 使用 contains 方法来检查迭代器中是否包含某个元素
    val containsElement = iterator.contains(2)
    println(s"Contains element: $containsElement")
  }
}
输出:
Has more elements: true
Next element: 1
Size: 2
Size1: 0
Contains element: false

特别注意:迭代器是一次性的,所以在使用完毕后就不能再次使用。因此,在上面的代码中,我们在调用 next 方法后就不能再使用其他方法来访问迭代器中的元素了。所以 size1输出为0

Tuple

Tuple从集合中抽出来讲述是因为Tuple不属于集合。它是一种用来将多个值组合在一起的数据结构。一个Tuple可以包含不同类型的元素,每个元素都有一个固定的位置。Scala 中的元组包含一系列类:Tuple2,Tuple3等,直到 Tuple22。

示例如下:

object Main {
  def main(args: Array[String]): Unit = {
    // 定义一个包含两个元素的Tuple
    val tuple1 = (1, "hello")
    println(tuple1)
    // 定义一个包含三个元素的Tuple
    val tuple2 = (1, "hello", true)
    println(tuple2)
    // 定义一个包含多个不同类型元素的Tuple
    val tuple3 = (1, "hello", true, 3.14)
    println(tuple3)
    // 访问Tuple中的元素
    val firstElement = tuple3._1
    val secondElement = tuple3._2
    println(s"first element: $firstElement, second element: $secondElement")
  }
}
输出:
(1,hello)
(1,hello,true)
(1,hello,true,3.14)
first element: 1, second element: hello

下面是一些Tuple的常用方法:

object Main {
  def main(args: Array[String]): Unit = {
    val tuple = (1, "hello")
    // 交换二元组的元素
    // 输出:(hello,1)
    val swapped = tuple.swap
    // 使用 copy 方法来创建一个新的 Tuple,其中某些元素被替换为新值
    //输出:(1,world)
    val newTuple = tuple.copy(_2 = "world")
    // 遍历元素
    // 输出: 1 hello
    tuple.productIterator.foreach(println)
    // 转换为字符串
    // 输出: (1,hello)
    val stringRepresentation = tuple.toString
    // 使用 Tuple.productArity 方法来获取 Tuple 中元素的个数
    // 输出: 2
    val arity = tuple.productArity
    // 使用 Tuple.productElement 方法来访问 Tuple 中的元素
    // 输出: 1
    val firstElement = tuple.productElement(0)
  }
}

提取器对象

提取器对象是一个包含有 unapply 方法的单例对象。apply 方法就像一个构造器,接受参数然后创建一个实例对象,反之 unapply 方法接受一个实例对象然后返回最初创建它所用的参数。提取器常用在模式匹配和偏函数中。

下面是一个使用提取器对象(Extractor Object)的 Scala 代码示例:

object Email {
  def apply(user: String, domain: String): String = s"$user@$domain"
  def unapply(email: String): Option[(String, String)] = {
    val parts = email.split("@")
    if (parts.length == 2) Some(parts(0), parts(1))
    else None
  }
}
// 测试
val address = "john.doe@example.com"
address match {
  case Email(user, domain) => println(s"User: $user, Domain: $domain")
  case _ => println("Invalid email address")
}

在上述示例中,定义了一个名为Email的提取器对象。提取器对象具有两个方法:applyunapply

apply方法接收用户名和域名作为参数,并返回一个完整的电子邮件地址。在这个示例中,我们简单地将用户名和域名拼接成电子邮件地址的字符串。

unapply方法接收一个电子邮件地址作为参数,并返回一个Option类型的元组。在这个示例中,我们使用split方法将电子邮件地址分割为用户名和域名两部分,并通过Some将它们封装到一个Option中返回。如果分割后的部分不是两部分,即电子邮件地址不符合预期的格式,我们返回None

在测试部分,我们创建了一个电子邮件地址字符串address。然后,我们使用match表达式将address与提取器对象Email进行匹配。如果匹配成功,我们提取出用户名和域名,并打印出对应的信息。如果匹配失败,即电子邮件地址无效,我们打印出相应的错误信息。

流程判断

while和if

object Main {
  def main(args: Array[String]): Unit = {
    println("----while----")
    var i = 0
    while (i < 5) {
      println(i)
      i += 1
    }
    println("----if----")
    val x = 3
    if (x > 0) {
      println("x大于0")
    } else {
      println("x小于0")
    }
  }
}
输出:
----while----
0
1
2
3
4
----if----
x大于0

Scala中的while和if跟Java中的方法几乎没有区别。

for

object Main {
  def main(args: Array[String]): Unit = {
  println("----for循环----")
    for (i <- 1 to 5) {
      println(i)
    }
  }
}
输出:
----for循环----
1
2
3
4
5

for循环跟Java略微有点区别。其中i <- 1 to 5是Scala中for循环的一种常见形式。它表示遍历一个序列,序列中的元素依次为1、2、3、4、5。

多重for循环简写

Scala中对于多重for循环可以进行简写,例如我们要用Java写多重for循环是下面这样:

public class Main {
  public static void main(String[] args) {
    // 多重for循环
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 3; j++) {
        System.out.println(i + " " + j);
      }
    }
  }
}

而用Scala我们可以直接简写为下面这样:

object Main {
  def main(args: Array[String]): Unit = {
    // 多重for循环
    for (i <- 0 until 3; j <- 0 until 3) {
      println(i + " " + j)
    }
  }
}
输出:
0 0
0 1
0 2
1 0
1 1
1 2
2 0
2 1
2 2

可以看出scala的for循环语法更加的精简。代码行数更少。

yield

在for循环的过程中我们可以使用 yield来对for循环的元素进行 操作收集:

object Main {
  def main(args: Array[String]): Unit = {
    val numbers = for (i <- 1 to 5) yield i * 2
    println(numbers)
  }
}
输出:
Vector(2, 4, 6, 8, 10)

模式匹配(pattern matching)

在Scala语言中,没有switchcase关键字。相反,我们可以使用模式匹配(pattern matching)来实现类似于switch语句的功能。它是Java中的switch语句的升级版,同样可以用于替代一系列的 if/else 语句。下面是一个简单的例子,它展示了如何使用模式匹配来实现类似于switch语句的功能:

object Main {
  def main(args: Array[String]): Unit = {
    def matchTest(x: Any): String = x match {
      case 1 => "one"
      case "two" => "two"
      case y: Int => "scala.Int"
      case _ => "many"
    }
    println(matchTest(1))
    println(matchTest("two"))
    println(matchTest(3))
    println(matchTest("test"))
  }
}
输出:
one
two
scala.Int
many

在上面的例子中,定义了一个名为matchTest的函数,它接受一个类型为Any的参数x。在函数体中,我们使用了一个模式匹配表达式来匹配参数x的值。

在模式匹配表达式中,我们定义了四个case子句。第一个case子句匹配值为1的情况;第二个case子句匹配值为"two"的情况;第三个case子句匹配类型为Int的情况;最后一个case子句匹配所有其他情况。

样例类(case classes)的匹配

样例类非常适合用于模式匹配。

abstract class Notification
case class Email(sender: String, title: String, body: String) extends Notification
case class SMS(caller: String, message: String) extends Notification
def showNotification(notification: Notification): String = {
  notification match {
    case Email(sender, title, _) =>
      s"You got an email from $sender with title: $title"
    case SMS(number, message) =>
      s"You got an SMS from $number! Message: $message"
  }
}
val someSms = SMS("12345", "Are you there?")
val someEmail = Email("John Doe", "Meeting", "Are we still meeting tomorrow?")
println(showNotification(someSms))
println(showNotification(someEmail))

这段代码定义了一个抽象类 Notification,以及两个扩展自 Notification 的样例类 EmailSMS。然后定义了一个函数 showNotification,它接受一个 Notification 类型的参数,并使用模式匹配来检查传入的通知是 Email 还是 SMS,并相应地生成一条消息。

最后,我们创建了两个实例:一个 SMS 和一个 Email,并使用 showNotification 函数来显示它们的消息。

模式守卫(Pattern guards)

为了让匹配更加具体,可以使用模式守卫,也就是在模式后面加上if <boolean expression>

def checkNumberType(number: Int): String = number match {
  case n if n > 0 && n % 2 == 0 => "Positive even number"
  case n if n > 0 && n % 2 != 0 => "Positive odd number"
  case n if n < 0 && n % 2 == 0 => "Negative even number"
  case n if n < 0 && n % 2 != 0 => "Negative odd number"
  case _ => "Zero"
}
// 测试
println(checkNumberType(10))    // 输出: Positive even number
println(checkNumberType(15))    // 输出: Positive odd number
println(checkNumberType(-4))    // 输出: Negative even number
println(checkNumberType(-9))    // 输出: Negative odd number
println(checkNumberType(0))     // 输出: Zero

在上述示例中,我们定义了一个名为checkNumberType的方法,它接收一个整数参数number并返回一个描述数字类型的字符串。

通过使用模式守卫,我们可以对number进行多个条件的匹配,并根据条件来返回相应的结果。在每个case语句中,我们使用模式守卫来进一步过滤匹配的数字。

例如,case n if n > 0 && n % 2 == 0 表示当 number 大于 0 且为偶数时执行该分支。类似地,其他的 case 语句也使用了模式守卫来进行更精确的匹配。

在测试部分,我们调用了checkNumberType方法并传入不同的整数进行测试。根据不同的输入,方法将返回相应的字符串描述数字类型。

仅匹配类型

当不同类型对象需要调用不同方法时,仅匹配类型的模式非常有用

def processValue(value: Any): String = value match {
  case str: String => s"Received a String: $str"
  case num: Int => s"Received an Int: $num"
  case lst: List[_] => s"Received a List: $lst"
  case _: Double => "Received a Double"
  case _ => "Unknown value"
}
// 测试
println(processValue("Hello"))                // 输出: Received a String: Hello
println(processValue(10))                     // 输出: Received an Int: 10
println(processValue(List(1, 2, 3)))           // 输出: Received a List: List(1, 2, 3)
println(processValue(3.14))                    // 输出: Received a Double
println(processValue(true))                    // 输出: Unknown value

在上述示例中,定义了一个名为processValue的方法,它接收一个任意类型的参数value,并返回一个描述值类型的字符串。

通过使用类型模式匹配,我们可以根据不同的值类型来执行相应的逻辑。在每个case语句中,我们使用类型模式匹配来匹配特定类型的值。

例如,case str: String 表示当 value 的类型为 String 时执行该分支,并将其绑定到变量 str。类似地,其他的 case 语句也使用了类型模式匹配来匹配不同的值类型。

在测试部分,我们调用了processValue方法并传入不同类型的值进行测试。根据值的类型,方法将返回相应的描述字符串。

Scala的模式匹配是我觉得非常实用和灵活的一个功能,比Java的switch语句更加强大和灵活。Scala的模式匹配可以匹配不同类型的值,包括数字、字符串、列表、元组等。而Java的switch语句只能匹配整数、枚举和字符串类型的值

密封类

特质(trait)和类(class)可以用sealed标记为密封的,这意味着其所有子类都必须与之定义在相同文件中,从而保证所有子类型都是已知的。密封类限制了可扩展的子类类型,并在模式匹配中确保所有可能的类型都被处理,提高了代码的安全性和可靠性。

下面是一个使用密封类(sealed class)和模式匹配的 Scala 代码示例:

sealed abstract class Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape
case class Square(side: Double) extends Shape
def calculateArea(shape: Shape): Double = shape match {
  case Circle(radius) => math.Pi * radius * radius
  case Rectangle(width, height) => width * height
  case Square(side) => side * side
}
// 测试
val circle = Circle(5.0)
val rectangle = Rectangle(3.0, 4.0)
val square = Square(2.5)
println(s"Area of circle: ${calculateArea(circle)}")         // 输出: Area of circle: 78.53981633974483
println(s"Area of rectangle: ${calculateArea(rectangle)}")   // 输出: Area of rectangle: 12.0
println(s"Area of square: ${calculateArea(square)}")         // 输出: Area of square: 6.25

在上述示例中,我们定义了一个密封类Shape,它是一个抽象类,不能直接实例化。然后,我们通过扩展Shape类创建了CircleRectangleSquare这三个子类。

calculateArea方法中,我们使用模式匹配对传入的shape进行匹配,并根据不同的Shape子类执行相应的逻辑。在每个case语句中,我们根据具体的形状类型提取相应的属性,并计算出面积。

在测试部分,我们创建了一个Circle对象、一个Rectangle对象和一个Square对象,并分别调用calculateArea方法计算它们的面积。

嵌套方法

当在Scala中定义一个方法时,我们可以选择将其嵌套在另一个方法内部。这样的嵌套方法只在外部方法的作用域内可见,而对于外部方法以外的代码是不可见的。这可以帮助我们组织和封装代码,提高代码的可读性和可维护性。

def calculateDiscountedPrice(originalPrice: Double, discountPercentage: Double): Double = {
  def applyDiscount(price: Double, discount: Double): Double = {
    val discountedPrice = price - (price * discount)
    discountedPrice
  }
  def validateDiscount(discount: Double): Double = {
    val maxDiscount = 0.8 // 最大折扣为80%
    if (discount > maxDiscount) {
      maxDiscount
    } else {
      discount
    }
  }
  val validatedDiscount = validateDiscount(discountPercentage)
  val finalPrice = applyDiscount(originalPrice, validatedDiscount)
  finalPrice
}
// 调用外部方法
val price = calculateDiscountedPrice(100.0, 0.9)
println(s"The final price is: $price")

在上述示例中,定义了一个外部方法calculateDiscountedPrice,它接收原始价格originalPrice和折扣百分比discountPercentage作为参数,并返回最终价格。

calculateDiscountedPrice方法的内部,我们定义了两个嵌套方法:applyDiscountvalidateDiscountapplyDiscount方法用于计算折扣后的价格,它接收价格和折扣作为参数,并返回折扣后的价格。validateDiscount方法用于验证折扣百分比是否超过最大折扣限制,并返回一个有效的折扣百分比。

在外部方法中,我们首先调用validateDiscount方法来获取有效的折扣百分比,然后将其与原始价格一起传递给applyDiscount方法,计算最终价格。最后,我们打印出最终价格。

正则表达式模型

正则表达式是用来找出数据中的指定模式(或缺少该模式)的字符串。.r方法可使任意字符串变成一个正则表达式。

object Main extends App {
  val emailPattern = "([a-zA-Z0-9_.+-]+)@([a-zA-Z0-9-]+)\\.([a-zA-Z0-9-.]+)".r
  def validateEmail(email: String): Boolean = email match {
    case emailPattern(username, domain, extension) =>
      println(s"Valid email address: $email")
      true
    case _ =>
      println(s"Invalid email address: $email")
      false
  }
  // 测试
  validateEmail("john.doe@example.com")        // 输出: Valid email address: john.doe@example.com
  validateEmail("jane.doe@invalid")            // 输出: Invalid email address: jane.doe@invalid
}

在上述示例中,我们首先创建了一个名为emailPattern的正则表达式对象,用于匹配电子邮件地址的模式。

然后,定义了一个名为validateEmail的方法,它接收一个字符串类型的电子邮件地址作为参数,并使用正则表达式模式匹配来验证电子邮件地址的有效性。

在模式匹配的case语句中,我们使用emailPattern对传入的电子邮件地址进行匹配,并将匹配结果中的用户名、域名和扩展提取到相应的变量中。如果匹配成功,我们打印出验证通过的消息,并返回true表示电子邮件地址有效。如果没有匹配成功,则打印出验证失败的消息,并返回false表示电子邮件地址无效。

在测试部分,我们调用validateEmail方法分别传入一个有效的电子邮件地址和一个无效的电子邮件地址进行测试。根据匹配结果,我们打印出相应的验证消息。

型变

在 Scala 中,协变(covariance)和逆变(contravariance)是用来描述类型参数在子类型关系中的行为的概念。协变和逆变是用来指定泛型类型参数的子类型关系的方式,以确保类型安全性。

协变

协变(Covariance): 协变表示类型参数在子类型关系中具有相同的方向。如果一个泛型类的类型参数是协变的,那么子类型的关系将保持不变,即父类型可以被替换为子类型。在 Scala 中,可以使用 + 符号来表示协变。

下面是一个使用协变的示例代码,使用 + 符号表示类型参数 A 是协变的:

class Animal
class Dog extends Animal
class Cage[+A]
val dogCage: Cage[Dog] = new Cage[Dog]
val animalCage: Cage[Animal] = dogCage

在上述示例中,我们定义了一个协变类 Cage[+A],它接受一个类型参数 A,并使用 + 符号来表示 A 是协变的。我们创建了一个 dogCage,它是一个 Cage[Dog] 类型的实例。然后,我们将 dogCage 赋值给一个类型为 Cage[Animal] 的变量 animalCage,这是合法的,因为 Cage[+A] 的协变性允许我们将子类型的 Cage 赋值给父类型的 Cage

逆变

逆变(Contravariance): 逆变表示类型参数在子类型关系中具有相反的方向。如果一个泛型类的类型参数是逆变的,那么子类型的关系将反转,即父类型可以替换为子类型。在 Scala 中,可以使用 - 符号来表示逆变。

下面是一个使用逆变的示例代码,使用 - 符号表示类型参数 A 是逆变的:

class Animal
class Dog extends Animal
class Cage[-A]
val animalCage: Cage[Animal] = new Cage[Animal]
val dogCage: Cage[Dog] = animalCage

在上述示例中,定义了一个逆变类 Cage[-A],它接受一个类型参数 A,并使用 - 符号来表示 A 是逆变的。我们创建了一个 animalCage,它是一个 Cage[Animal] 类型的实例。然后,我们将 animalCage 赋值给一个类型为 Cage[Dog] 的变量 dogCage,这是合法的,因为 Cage[-A] 的逆变性允许我们将父类型的 Cage 赋值给子类型的 Cage。 通过协变和逆变,我们可以在 Scala 中实现更灵活的类型关系,并确保类型安全性。这在处理泛型集合或函数参数时特别有用。下面是一个更具体的示例:

abstract class Animal {
  def name: String
}
class Dog(val name: String) extends Animal {
  def bark(): Unit = println("Woof!")
}
class Cat(val name: String) extends Animal {
  def meow(): Unit = println("Meow!")
}
class Cage[+A](val animal: A) {
  def showAnimal(): Unit = println(animal.name)
}
def printAnimalNames(cage: Cage[Animal]): Unit = {
  cage.showAnimal()
}
val dog: Dog = new Dog("Fido")
val cat: Cat = new Cat("Whiskers")
val dogCage: Cage[Dog] = new Cage[Dog](dog)
val catCage: Cage[Cat] = new Cage[Cat](cat)
printAnimalNames(dogCage) // 输出:Fido
printAnimalNames(catCage) // 输出:Whiskers

在上述示例中,定义了一个抽象类 Animal,以及它的两个子类 DogCatDogCat 类都实现了 name 方法。

然后,定义了一个协变类 Cage[+A],它接受一个类型参数 A,并使用协变符号 + 表示 A 是协变的。Cage 类有一个名为 animal 的属性,它的类型是 A,也就是动物的类型。我们定义了一个名为 showAnimal() 的方法,它打印出 animal 的名称。

接下来,定义了一个名为 printAnimalNames() 的函数,它接受一个类型为 Cage[Animal] 的参数,并打印出其中动物的名称。

我们创建了一个 Dog 类型的对象 dog 和一个 Cat 类型的对象 cat。然后,我们分别创建了一个 Cage[Dog] 类型的 dogCage 和一个 Cage[Cat] 类型的 catCage

最后,我们分别调用 printAnimalNames() 函数,并传入 dogCagecatCage。由于 Cage 类是协变的,所以可以将 Cage[Dog]Cage[Cat] 赋值给 Cage[Animal] 类型的参数,而不会产生类型错误。

相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
目录
相关文章
|
2月前
|
分布式计算 大数据 Java
大数据-87 Spark 集群 案例学习 Spark Scala 案例 手写计算圆周率、计算共同好友
大数据-87 Spark 集群 案例学习 Spark Scala 案例 手写计算圆周率、计算共同好友
52 5
|
2月前
|
分布式计算 关系型数据库 MySQL
大数据-88 Spark 集群 案例学习 Spark Scala 案例 SuperWordCount 计算结果数据写入MySQL
大数据-88 Spark 集群 案例学习 Spark Scala 案例 SuperWordCount 计算结果数据写入MySQL
51 3
|
2月前
|
消息中间件 分布式计算 NoSQL
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
42 0
|
2月前
|
消息中间件 存储 分布式计算
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
93 0
|
2月前
|
Java 大数据 数据库连接
大数据-163 Apache Kylin 全量增量Cube的构建 手动触发合并 JDBC 操作 Scala
大数据-163 Apache Kylin 全量增量Cube的构建 手动触发合并 JDBC 操作 Scala
31 2
大数据-163 Apache Kylin 全量增量Cube的构建 手动触发合并 JDBC 操作 Scala
|
2月前
|
分布式计算 大数据 Java
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
28 1
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
|
2月前
|
消息中间件 存储 druid
大数据-156 Apache Druid 案例实战 Scala Kafka 订单统计
大数据-156 Apache Druid 案例实战 Scala Kafka 订单统计
42 3
|
2月前
|
SQL 分布式计算 Java
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
41 0
|
2月前
|
缓存 分布式计算 大数据
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(一)
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(一)
50 0
|
2月前
|
分布式计算 算法 大数据
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(二)
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(二)
52 0