spark之scala学习——11步走进scala<转>

9 篇文章 0 订阅
scala和java或.NET都可以协同工作。Scala的主编译器,scalac,会生成在jvm上运行的class文件。然而,另一个编辑器则生成能在.NET CLR上运行的二进制文件。
scala和java代码可以实现互操作;

1. 下载、解压、环境变量;

2. 进入shell:

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. will@will-vm1:~$ scala  
  2. Welcome to Scala version 2.11.0-RC1 (Java HotSpot(TM) Client VM, Java 1.6.0_45).  
  3. Type in expressions to have them evaluated.  
  4. Type :help for more information.  
  5.   
  6. scala>   

可以直接把scala当做计算器使用:

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. scala> 1 + 2  
  2. res0: Int = 3  
  3.   
  4. scala> res0 * 4  
  5. res1: Int = 12  

res0是系统默认创建的一个临时变量,在后面也可以使用它,res1也是。

3. 定义变量(val , var)

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. scala> val msg2:java.lang.String = "hello world"  
  2. msg2: String = hello world      //对常量的描述  
  3.   
  4. scala> val msg3:String = "hello world"  
  5. msg3: String = hello world      //可以自动识别类型,进行类型推理(type inference)  
  6.   
  7. scala> msg2 = "dfs"          //val相当于final类型,immutable  
  8. <console>:8: error: reassignment to val  
  9.        msg2 = "dfs"  
  10.             ^  
  11.   
  12. scala> var msg3:String = "hello world"  
  13. msg3: String = hello world  
  14.   
  15. scala> msg3 = "hello yet"        //var相当于变脸,mutable  
  16. msg3: String = hello yet  

4. 定义方法(def)

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. scala> def max3(x: Int, y: Int)={if (x>y) x else y}   //带参数的方法,返回值类型可以自己识别,不过良好的代码习惯,最好指定def max3(x: Int, y: Int) : Int={if (x>y) x else y},对于递归方法,必须指定!  
  2.                             /* 参数必须指定类型,貌似scala唯独不会对方法的参数进行类型推理 */  
  3. max3: (x: Int, y: Int)Int       //对方法的描述  
  4.   
  5. scala> max3(234,3)  
  6. res8: Int = 234  
  7.   
  8. scala> max3(234,334)  
  9. res9: Int = 334  
  10.   
  11. scala> def greet()={println("hello world")}      //无参数的方法  
  12. greet: ()Unit  
  13.   
  14. scala> greet  
  15. hello world  
  16.   
  17. scala> greet()               //推荐编写习惯  
  18. hello world  
5. scala脚本
hello.scala,内容是println("Hello, world, from a script!")
运行脚本:
>scala hello.scala
在unix系统中,也可以使用shell来调用此方法
scalatest.sh的内容:

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. exec scala $0 $@  
  3. !#  
  4. // Say hello to the first argument  
  5. println("Hello, " + args(0) + "!")  

>./scalatest.sh WILL

6. 过程控制
while和if

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. var i = 0;  
  2. while (i < args.length) {  
  3.   if (i != 0) {  
  4.     print(" ");             //分号可有可无,良好习惯应该是有  
  5.   }  
  6.   print(args(i));  
  7.   i += 1;  
  8. }  
  9. println();  

foreach以及for的使用:

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. scala> myNumbers.foreach(x => println(x))  
  2. 1  
  3. 2  
  4. 3  
  5.   
  6. scala> myNumbers.foreach((x: Int) => println(x))  
  7. 1  
  8. 2  
  9. 3  
  10.   
  11. scala> myNumbers.foreach(println())  
  12. <console>:9: error: type mismatch;  
  13.  found   : Unit  
  14.  required: Int => ?  
  15.               myNumbers.foreach(println())  
  16.                                        ^  
  17.   
  18. scala> myNumbers.foreach(println)    //If an anonymous function consists of one method application that takes a single argument,you need not explicitly name and specify the argument  
  19. 1  
  20. 2  
  21. 3  
  22.   
  23. scala> myNumbers.foreach((x:Int) => print(cube(x)))       //cube是前面自己定义的函数—— defcube(l : Int) : Int = {l*l*l}  
  24. 1827  
  25. scala> myNumbers.foreach((x:Int) => println(cube(x)))  
  26. 1  
  27. 8  
  28. 27  
  29.   
  30.   
  31. scala> for (num <- myNumbers) println(num)    // <- 等同于 in,后面的block应该用{}包起来最好  
  32. 1  
  33. 2  
  34. 3  

7. 指定类型的数组Array

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. val greetStrings: Array[String] = new Array<strong>[String]</strong>(3)  
  2. greetStrings(0) = "Hello"  
  3. greetStrings(1) = ", "  
  4. greetStrings(2) = "world!\n"  
  5. for (i <- 0<strong> to</strong> 2)           //to 相当于 0.<strong>to(2)</strong> __scala没有操作符运算,所有的运算的都是转化成对象中对应的方法来执行。  
  6.   print(greetStrings(i))  
scala没有操作符运算,所有的运算都是转化成对象中对应的方法来执行。例如:
greetStrings(0) = "Hello" 等同于greetStrings .update (0,"Hello")
print(greetString(i)) 等同于 print(greetStrings .apply(i) )
scala架构把所有东西都看做带有方法的对象。只有这些带有方法的对象,没有其他东西。


8. Lists和Tuples

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. scala> val oneTwo = List(1, 2)  
  2. oneTwo: List[Int] = List(1, 2)  
  3.   
  4. scala> val threeFour = List(3, 4)  
  5. threeFour: List[Int] = List(3, 4)  
  6.   
  7. scala> val oneTwoThreeFour = oneTwo ::: threeFour        //::: 连接两个List  
  8. oneTwoThreeFour: List[Int] = List(1, 2, 3, 4)  
  9. scala> println(oneTwo + " and" + threeFour + " were not mutated.")  
  10. List(1, 2) and List(3, 4) were not mutated.  
  11.    
  12. scala> println("Thus, " +oneTwoThreeFour + " is a new List.")  
  13. Thus, List(1, 2, 3, 4) is a new List.  
面向过程的好处:a. 方法与变量可靠、可重用;b. 所有进出方法的对象都要经过严格的类型检查。
这需要这些对象不可变,就像java中的String。在scala中Array本身是不可变的,但是其中的元素可变,所以它也是可变的。鉴于此,一个更加面向过程的、有序的对象就是List了。
创建一个List:val oneTwoThree= List(1, 2, 3)。这里不用new,因为单例scala.List中已经把List定义成了一个工厂方法。由于List是不可变的,所以:::之后的对象是有一个新的List。
还有一个临时向已有List中添加元素的方法:

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. val twoThree = List(2, 3)  
  2. val oneTwoThree = 1 :: twoThree             //:: 只能在头部添加新元素  
  3. println(oneTwoThree)  
  4. List(1, 2, 3)  
  5. scala> val one_fouer = oneTwoThree :: 4          //尾部报错  
  6. <console>:9: error: value :: is not a member of Int  
  7.        val one_fouer = oneTwoThree :: 4  
  8.                                    ^  
  9.   
  10. scala> val one_fouer = 4 :: oneTwoThree          //头部无恙  
  11. one_fouer: List[Int] = List(4, 1, 2, 3)  
  12.   
  13. tuples可以包含不同类型的元素,不同于List以及Array。  
  14. scala> val pair =(99, "Luftballons")  
  15. pair: (Int, String) =(99,Luftballons)                       //类型可以在定义中自动识别,取决于参数  
  16.    
  17. scala>println(pair._1)  
  18. 99  
  19.    
  20. scala>println(pair._2)  
  21. Luftballons  

9. Set和Maps
它们都有可变和不可变的包,使用的类名确实相同的。如:scala.collection.mutable.HashSet,scala.collection.immutable.HashSet

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. importscala.collection.mutable.HashSet  
  2.    
  3. val jetSet = newHashSet<strong>[String]</strong>     //指定类型  
  4. jetSet +="Lear"  
  5. jetSet +=("Boeing", "Airbus")  
  6. println(jetSet.contains("Cessna"))    //false  
  7.    
  8. ----------------------------------------------------------  
  9. importscala.collection.mutable.HashMap  
  10.    
  11. val treasureMap=new HashMap<strong>[Int, String]</strong>  
  12. treasureMap +=1-> "Go to island."    //.+=()  
  13. treasureMap +=2-> "Find big X on ground."  
  14. treasureMap +=3-> "Dig."  
  15. println(treasureMap(2))  
  16.    
  17. ---------------------------------------------------------------  
  18.    
  19. // Innumerals.scala有一个Map的工厂方法  
  20. val romanNumeral=Map(1 -> "I", 2 -> "II", 3 -> "III",4-> "IV", 5 -> "V")          //默认使用scala.collection.immutable.Map  
  21. println(romanNumeral(4))  

10. 理解类和单例类
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. classFancyGreeter(greeting: String) {                       //直接在定义类时指定构造函数的参数  
  2.    
  3.   def this(greeting:String) = this(greeting, 1)     //相当于辅助构造函数,也可以在定义时使用,实现多个构造函数的机制  
  4.   if (greeting ==null) {                             //在类的body中进行参数检查,初始化实例时执行  
  5.    throw newNullPointerException("greeting was null")  
  6.   }  
  7.    
  8.   def greet() = println(greeting)  
  9. }  
  10.    
  11. val g = newFancyGreeter("Salutations, world")  
  12. g.greet  
  13. <span style="font-family: Arial, Helvetica, sans-serif;">  
  14. </span>  
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-family: Arial, Helvetica, sans-serif;">scala中没有静态变量或方法,一般使用一个辅助此类的单例类来解决,也是它的同伴</span>  
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. // TheWorldlyGreeter class  
  2. class <span style="color:#6600cc;">WorldlyGreeter</span>(greeting: String) {             //普通类是class  
  3.   def greet() = {  
  4.     val worldlyGreeting =WorldlyGreeter.worldify(greeting)  //直接使用单例类中的方法  
  5.     println(worldlyGreeting)  
  6.   }  
  7. }  
  8.    
  9. // TheWorldlyGreeter companion object                      //合作者  
  10. object <span style="color:#3333ff;">WorldlyGreeter </span>{                                      //单例类的关键字是object  
  11.   def worldify(s: String) = s + ",world!"  
  12. }  
  13.    
  14.    
  15. /* 以上的定义必须需要一个主类来跑才行。使用一个单例类中的main方法来作为app的入口,这个类可以称为独行者,因为没有其他类用它  
  16.  * 所以,单例类包括独行者和合作者两种  
  17.  */  
  18. object WorldlyApp {  
  19.   defmain(args: Array[String]) {  
  20.    val wg = newWorldlyGreeter("Hello")  
  21.    wg.greet()  
  22.   }  
  23. }  
要运行上面的入口类,不能仅仅通过scala WorldlyGreeter.scala,因为它以定义一个东西作为类的结束。一般scala默认返回最后一行的值,定义是没有值的。我们必须要编译这些类,并且运行它才行。
scala提供两种编译工具:scalac和fsc。相对于前者,fsc会在第一次连接jvm后建立一个守护线程,以后可以更快速地事先编译。他俩的使用方法相同:fsc WorldlyApp.scalaWorldlyGreeter.scala
执行过程是:
      a. 以WorldlyApp的main方法为入口启动jvm; 
      b.   WorldlyApp的main方法用new建立一个Greeter对象,把参数“hello”传进去:
      c.  Greeter的主构造方法使用“hello”初始化;
      d.  WorldlyApp将创建的Greeter实例赋值给本地wg;
      e.  激发Greeter实例的greet方法;
     f.   Greeter实例的方法激发单例类Greeter的greet方法;
     h.  单例类返回值,一次传递回去


11. 理解 trait和mixin
trait,官网说类似于java中的接口,我个人倒感觉更像抽象类,因为它可以有方法体,而且相互之间的继承使用extends。唯一像interface的地方是,可以多继承。

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <strong>trait</strong> Friendly {  
  2.   def greet() = "Hi"     //可以有方法体  
  3. }  
  4.    
  5. class Dog <strong>extends </strong>Friendly {  
  6.   override def greet() = "Woof"  
  7. }  
  8.    
  9. class HungryCat <strong>extends </strong>Friendly {  
  10.   <strong><span style="color:#cc66cc;">override </span></strong>def greet() = "Meow"  
  11. }  
  12.    
  13. class HungryDog extends Dog {  
  14.   <strong><span style="color:#cc66cc;">override </span></strong>def greet() = "I'd like to eatmy own dog food"  //覆盖方法使用override,在def的前面  
  15. }  
  16.    
  17. <strong>trait</strong> ExclamatoryGreeter <strong>extends </strong>Friendly {  //多继承  
  18.   <strong><span style="color:#cc66cc;">override </span></strong>def greet() = super.greet() +"!"  
  19. }  
  20.    
  21. var pet: Friendly =new Dog  
  22. println(pet.greet())  
  23.    
  24. pet = new HungryCat  
  25. println(pet.greet())  
  26.    
  27. pet = new HungryDog  
  28. println(pet.greet())  
  29.    
  30. pet = new Dog <strong>with</strong> ExclamatoryGreeter   //会先继承with后面的ExclamatoryGreeter,然后再实现Dog对ExclamatoryGreeter的继承与重写  
  31. println(pet.greet())  
  32.    
  33. pet = new HungryCat <strong>with</strong> ExclamatoryGreeter  
  34. println(pet.greet())  
  35.    
  36. pet = new HungryDog <strong>with</strong> ExclamatoryGreeter  
  37. println(pet.greet())  
  38. When you run the friendly.scala script, it will print:  
  39. Woof  
  40. Meow  
  41. I'd like to eat myown dog food  
  42. Woof!  
  43. Meow!  
  44. I'd like to eat myown dog food!  

总结:
scala承诺的高效与简洁可见一斑,可以提高java程序员的生成效率。目前,应该可以使用scala完成一些简单的任务,运行scala脚本什么的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 批量操作是指一次性对多个数据行操作,可以提高操作效率。在使用 Spark 读写 HBase 时,也可以使用批量操作来提高效率。具体实现方式如下: 1. 批量写入数据 使用 HBase 的 Put 类来创建要写入的数据,然后将 Put 对象添加到一个 List 中,最后使用 HBase 的 Table 类的 put 方法来批量写入数据。示例代码如下: ```scala val conf = HBaseConfiguration.create() val connection = ConnectionFactory.createConnection(conf) val table = connection.getTable(TableName.valueOf("table_name")) val puts = new ListBuffer[Put]() for (i <- 1 to 100) { val put = new Put(Bytes.toBytes(s"row_$i")) put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col"), Bytes.toBytes(s"value_$i")) puts += put } table.put(puts.toList.asJava) ``` 2. 批量读取数据 使用 HBase 的 Get 类来创建要读取的数据,然后将 Get 对象添加到一个 List 中,最后使用 HBase 的 Table 类的 get 方法来批量读取数据。示例代码如下: ```scala val conf = HBaseConfiguration.create() val connection = ConnectionFactory.createConnection(conf) val table = connection.getTable(TableName.valueOf("table_name")) val gets = new ListBuffer[Get]() for (i <- 1 to 100) { val get = new Get(Bytes.toBytes(s"row_$i")) get.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col")) gets += get } val results = table.get(gets.toList.asJava) for (result <- results) { val row = Bytes.toString(result.getRow) val value = Bytes.toString(result.getValue(Bytes.toBytes("cf"), Bytes.toBytes("col"))) println(s"$row: $value") } ``` 以上就是使用 Scala 实现 Spark 读写 HBase 的批量操作的方法。 ### 回答2: 在实际的数据处理中,一次需要对多条数据行读写操作,如果每次都行单条的读写逐条操作会使程序效率非常低下。所以spark提供了批量操作API,可以对多条数据行一次性的读写操作,极大地提高了程序的效率。 批量读操作: 批量读取数据的方式有两种:Get和Scan。 使用Get方式读取多条数据,需要将每条数据对应的Get对象添加到List集合当中,再将List集合换为RDD对象行操作。示例代码如下: ```scala val conf = HBaseConfiguration.create() conf.set(TableInputFormat.INPUT_TABLE, tableName) val gets = new util.ArrayList[Get]() gets.add(new Get(Bytes.toBytes("rowkey1"))) gets.add(new Get(Bytes.toBytes("rowkey2"))) gets.add(new Get(Bytes.toBytes("rowkey3"))) conf.set(TableInputFormat.SCAN, convertScanToString(new Scan())) val getRdd = sc.parallelize(gets) val hbaseRdd = getRdd.map((_, null)).hbaseBulkGet(conf, tableName, (result: Result) => { val kv: Array[Byte] = result.getValue(Bytes.toBytes(family), Bytes.toBytes(column)) Bytes.toString(kv) }) println(hbaseRdd.collect.toBuffer) ``` 使用Scan方式读取多条数据,需要将Scan对象作为参数传入,再将RDD对象换为PairRDD并使用hbaseScan方法行操作。示例代码如下: ```scala val conf = HBaseConfiguration.create() conf.set(TableInputFormat.INPUT_TABLE, tableName) val scan = new Scan(Bytes.toBytes("rowkey1"), Bytes.toBytes("rowkey3")) conf.set(TableInputFormat.SCAN, convertScanToString(scan)) val hbaseRdd = sc.hbaseScanRDD(conf).map((result: Result) => { val kv: Array[Byte] = result.getValue(Bytes.toBytes(family), Bytes.toBytes(column)) Bytes.toString(kv) }) println(hbaseRdd.collect.toBuffer) ``` 批量写操作: 批量写操作可以使用Put对象集合,将多条数据对应的Put对象添加到集合中,并将集合换成RDD行操作即可。示例代码如下: ```scala val conf = HBaseConfiguration.create() conf.set(TableOutputFormat.OUTPUT_TABLE, tableName) val puts = new util.ArrayList[Put]() puts.add(new Put(Bytes.toBytes("rowkey1")).addColumn(Bytes.toBytes(family), Bytes.toBytes(column), Bytes.toBytes("value1"))) puts.add(new Put(Bytes.toBytes("rowkey2")).addColumn(Bytes.toBytes(family), Bytes.toBytes(column), Bytes.toBytes("value2"))) puts.add(new Put(Bytes.toBytes("rowkey3")).addColumn(Bytes.toBytes(family), Bytes.toBytes(column), Bytes.toBytes("value3"))) val putRdd = sc.parallelize(puts) putRdd.hbaseBulkPut(conf, tableName) ``` 总结: 批量操作是Spark访问HBase的常见操作方式,在实际的实现过程中需要注意以下几点: 1、Get和Scan对象在HBase中读取数据的方式不一样,需要注意区分; 2、使用批量读操作可以大大提高程序效率,减少读写操作的时间消耗; 3、使用批量写操作需要合理规划写入的数据,避免出现数据冲突问题,影响程序的运行。 ### 回答3: 本篇文章将继续深入介绍如何使用Scala编码实现Spark读写操作HBase,具体涉及到HBase的批量操作。 一、Batch操作概述 在使用HBase行数据处理的时候,我们常常需要对一个或多个表行批量操作,批量操作即是针对 HBase的多行行插入、删除等操作,以此来实现在HBase操作上的高效处理。HBase提供了很多批量操作API,比如 Put、Get、Delete、Scan,这些API都是可以批量操作的。 在Spark中,我们同样可以使用类似的API对HBase行批量操作。本文将根据具体需求使用Spark实现HBase的批量操作。 二、批量操作的实现 Spark读写HBase时,使用RDD中的foreachPartition来对每个分区行处理,在该函数内使用HBase API行操作。关于批量操作,我们可以在每个分区中开启一个batch操作,将每个操作加入batch后,再提交即可。 例如,我们可以考虑实现一个批量put的功能,将RDD中的数据一批一批写入表中: ``` def insert(tableName: String, rdd: RDD[(String, String)]): Unit = { try{ rdd.foreachPartition({ iter => val conf = HBaseUtils.getHBaseConfiguration() conf.set(TableOutputFormat.OUTPUT_TABLE, tableName) val conn = ConnectionFactory.createConnection(conf) val table = conn.getTable(TableName.valueOf(tableName)) val puts = new java.util.ArrayList[Put]() iter.foreach { case (rowKey:String, value: String) => { // 构造put对象并append val put = new Put(Bytes.toBytes(rowKey)) put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columnQualifier), Bytes.toBytes(value)) puts.add(put) if (puts.size() >= batchSize) { // 多条记录组成的put对象,使用put(List<Put>)一次性写入 table.put(puts) puts.clear() } } } // 如果puts还有内容,再写一次 if (puts.size() > 0) { table.put(puts) puts.clear() } table.close() conn.close() }) } catch { case e: Exception => e.printStackTrace() } } ``` 在该方法中,我们使用foreachPartition遍历RDD中的每个分区,然后通过Connection来获取HBase表实例。 之后定义了一个用于存放Put的List,当List的大小大于等于batchSize时,就将这个List中的所有put操作提交给HBase执行。 最后,释放资源,并为大家展示如何调用这个方法: ``` val rdd: RDD[(String, String)] = ... val tableName: String = ... insert(tableName, rdd) ``` 使用这种方式实现批量put,我们可以将一批数据提交到HBase执行,从而提升写入效率。当然,对于其他批量操作也可以应用类似的方式。 三、总结 本文根据实际需求,结合Spark和HBase的特点,实现了一些常用的批量操作,为大家提供了一个快速、高效的HBase操作方案。批量操作的好处是,可以将多条记录一次性操作,请求与写入等待时间都会得到缩短,获得更高的效率。感兴趣的同学可以试试,在实际开发中应该会受益匪浅!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值